将它们存储为 ASCII 字符串,并使字段成为索引。
编辑
Egads,我碰巧在做一些事情,并遇到了这个。多么可耻的回答。那天我一定是有点心情。如果可以的话,我会删除它并继续前进。但是,这是不可能的,所以我将提供一个更新的片段。
首先,了解什么是“高效”的唯一方法是衡量,考虑程序时间和空间以及源代码复杂性和程序员工作量。
幸运的是,这很容易。
我写了一个非常简单的 OSX 应用程序。该模型由一个属性组成:identifier
.
如果您不将属性标记为索引,这些都不重要。创建商店会花费更多时间,但它会使查询速度更快。
另外,请注意,为二进制属性创建谓词与为字符串创建谓词完全相同:
fetchRequest.predicate =
[NSPredicate predicateWithFormat:@"identifier == %@", identifier];
该应用程序非常简单。首先,它创建 N 个对象,并为标识符属性分配一个 UUID。它每 500 个对象保存一次 MOC。然后我们将所有标识符存储到一个数组中并随机打乱它们。然后将整个 CD 堆栈完全拆除以将其全部从内存中删除。
接下来,我们再次构建堆栈,然后遍历标识符,并进行简单的获取。构造 fetch 对象,使用一个简单的谓词来获取该对象。所有这些都是在自动释放池中完成的,以尽可能保持每次提取的原始状态(我承认会与 CD 缓存进行一些交互)。这不是那么重要,因为我们只是比较不同的技术。
二进制标识符是 UUID 的 16 字节。
UUID String是一个36字节的字符串,调用[uuid UUIDString]的结果,看起来是这样的(B85E91F3-4A0A-4ABB-A049-83B2A8E6085E)。
Base64 String 是一个 24 字节的字符串,对 16 字节的 UUID 二进制数据进行 base-64 编码的结果,对于相同的 UUID,它看起来像这样 (uF6R80oKSrugSYOyqOYIXg==)。
Count 是该运行的对象数。
SQLite 大小是实际 sqlite 文件的大小。
WAL 大小是 WAL(预写日志)文件的大小 - 仅供参考......
create 是创建数据库的秒数,包括保存。
Query 是查询每个对象的秒数。
Data Type | Count (N) | SQLite Size | WAL Size | Create | Query
--------------+-----------+-------------+-----------+---------+---------
Binary | 100,000 | 5,758,976 | 5,055,272 | 2.6013 | 9.2669
Binary | 1,000,000 | 58,003,456 | 4,783,352 | 59.0179 | 96.1862
UUID String | 100,000 | 10,481,664 | 4,148,872 | 3.6233 | 9.9160
UUID String | 1,000,000 | 104,947,712 | 5,792,752 | 68.5746 | 93.7264
Base64 String | 100,000 | 7,741,440 | 5,603,232 | 3.0207 | 9.2446
Base64 String | 1,000,000 | 77,848,576 | 4,931,672 | 63.4510 | 94.5147
首先要注意的是,实际的数据库大小比存储的字节数(1,600,000 和 16,000,000)大得多——这对于数据库来说是可以预料的。额外存储的数量将在某种程度上与您的实际对象的大小有关……这个仅存储标识符,因此开销的百分比会更高)。
其次,在速度问题上,作为参考,执行相同的 1,000,000 个对象查询,但在 fetch 中使用 object-id 大约需要 82 秒(注意这与调用之间的明显差异,existingObjectWithID:error:
需要 0.3065 秒)。
您应该分析您自己的数据库,包括在运行代码上明智地使用工具。我想如果我进行多次运行,数字会有所不同,但它们非常接近,因此没有必要进行此分析。
但是,基于这些数字,让我们看看代码执行的效率测量。
- 正如预期的那样,存储原始 UUID 二进制数据在空间方面更有效。
- 创建时间非常接近(差异似乎基于创建字符串的时间和所需的额外存储空间)。
- 查询时间似乎几乎相同,二进制字符串似乎有点慢。我认为这是最初的关注点——对二进制属性进行查询。
Binary 大大赢得了空间,它可以被认为在创建时间和查询时间上都非常接近。如果我们只考虑这些,存储二进制数据显然是赢家。
源代码复杂性和程序员时间如何?
好吧,如果您使用的是现代版本的 iOS 和 OSX,则几乎没有区别,尤其是 NSUUID 上的简单类别。
但是,您需要考虑一个问题,那就是使用数据库中的数据是否容易。当您存储二进制数据时,很难对数据进行良好的可视化。
因此,如果出于某种原因,您希望数据库中的数据以对人类更有效的方式存储,那么将其存储为字符串是更好的选择。因此,您可能需要考虑 base64 编码(或其他一些编码——尽管记住它已经在 base-256 编码中)。
FWIW,这是一个示例类别,可以更轻松地访问 UUID 作为 NSData 和 base64 字符串:
- (NSData*)data
{
uuid_t rawuuid;
[self getUUIDBytes:rawuuid];
return [NSData dataWithBytes:rawuuid length:sizeof(rawuuid)];
}
- (NSString*)base64String
{
uuid_t rawuuid;
[self getUUIDBytes:rawuuid];
NSData *data = [NSData dataWithBytesNoCopy:rawuuid length:sizeof(rawuuid) freeWhenDone:NO];
return [data base64EncodedStringWithOptions:0];
}
- (instancetype)initWithBase64String:(NSString*)string
{
NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
if (data.length == sizeof(uuid_t)) {
return [self initWithUUIDBytes:data.bytes];
}
return self = nil;
}
- (instancetype)initWithString:(NSString *)string
{
if ((self = [self initWithUUIDString:string]) == nil) {
self = [self initWithBase64String:string];
}
return self;
}