【发布时间】:2014-03-17 19:10:50
【问题描述】:
使用 Objective-C 中单例的标准模式,ARC 仍然会在每次使用单例时自动生成保留和释放调用,即使我们知道对象永远不会被释放。在对性能敏感的代码中,这些 ARC 生成的调用会导致大量额外开销。有没有办法告诉编译器不要为单例生成保留/释放代码?
就我而言,我正在编写一个对性能敏感的日语文本解析器。作为解析的一部分,我经常需要访问一个 NSCharacterSet 汉字字符,我在 NSCharacterSet 的一个类别中定义了它。
+ (id)kanjiCharacterSet
{
static NSCharacterSet* kanjiCharacterSet = nil;
static dispatch_once_t onceToken;
dispatch_once( &onceToken, ^
{
NSRange range = { .location = 0x4e00, .length = 0x9faf - 0x4e00 };
kanjiCharacterSet = [NSCharacterSet characterSetWithRange:range];
} );
return kanjiCharacterSet;
}
我访问此字符集的最常见位置之一是检查字符串中是否存在汉字字符。此代码位于 NSString 上的一个类别中。
- (BOOL)containsKanji
{
return [self rangeOfCharacterFromSet:[NSCharacterSet kanjiCharacterSet]].location != NSNotFound;
}
当我通过分析器运行此程序时,在 [NSString containsKanji] 中花费的全部时间中约有 40% 用于保留/释放代码。至于[NSCharacterSet kanjiCharacterSet],不包括我们实际生成字符集的第一次调用,每次调用大约有80%的时间花在了保留/释放代码上。
如果我在objc_retain、obj_release 和objc_autorelease 中设置断点,我可以看到[NSCharacterSet kanjiCharacterSet] 的return 语句中添加了一个retain 和一个autorelease,@987654329 中添加了一个retain @当它接收到从[NSCharacterSet kanjiCharacterSet]返回的值时,当我们从[NSString containsKanji]返回时正在添加一个释放。似乎所有这些调用对于单身人士来说都是不必要的。有没有办法告诉编译器不要生成这些调用?
我找到了一种解决方法,即向[NSString containsKanji] 添加一个静态变量来存储字符集,但我很想找到一个更通用的解决方案。
【问题讨论】:
-
您是否分析调试配置或发布?在发布构建中启用优化后,编译器可以优化这些发布/自动释放调用。您也可以尝试在禁用 arc 的情况下编译文件 - 可能会减少开销
-
我正在分析一个发布版本。感谢您关于禁用 ARC 的建议。不幸的是,如果我在包含单例的文件上禁用 ARC,调用代码仍然会生成一个释放调用,就好像单例在返回之前被保留一样,这迫使我无论如何都要将保留写入单例代码. =/
-
另外,+ (id)kanjiCharacterSet;应该是 + (instancetype)kanjiCharacterSet;
-
我差点让它返回 instancetype,但我注意到 Apple 在 NSCharacterSet 返回 id 中包含的字符集单例,所以我想我会遵循他们的约定。我想我认为他们没有将它们转换为实例类型一定是有原因的。
-
您是否使用过 Core Foundation 的
CFString和CFCharacterSet?
标签: objective-c performance singleton automatic-ref-counting