我试图将错误缩小到最小的可重现情况,并发现了一些奇怪的东西。
考虑这段代码:
static NSString *staticString = nil;
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
if (staticString == nil) {
staticString = [[NSArray arrayWithObjects:@"1", @"2", @"3", nil] componentsJoinedByString:@","];
}
[pool drain];
NSLog(@"static: %@", staticString);
return 0;
}
我期待这段代码崩溃。相反,它记录:
2011-01-18 14:41:06.311 EmptyFoundation[61419:a0f] static: static:
但是,如果我将其更改NSLog()
为:
NSLog(@"static: %s", [staticString UTF8String]);
然后它确实崩溃了。
编辑更多信息:
排干游泳池后:
NSLog(@"static: %@", staticString); //this logs "static: static: "
NSLog(@"static: %@", [staticString description]); //this crashes
所以显然在字符串上调用一个方法足以让它崩溃。在那种情况下,为什么不直接记录字符串会导致它崩溃?不NSLog()
应该调用该-description
方法吗?
第二个“静态:”来自哪里?为什么这不崩溃?
结果:
凯文巴拉德和格雷厄姆李都是正确的。格雷厄姆正确地意识到这NSLog()
不是调用(正如我错误地假设的那样-description
),而凯文几乎肯定是正确的,这是复制格式字符串和va_list
周围的一个奇怪的堆栈相关问题。
NSLogging
并且NSString
不调用-description
. Graham 优雅地展示了这一点,如果您跟踪进行日志记录的 Core Foundation 资源,您会发现情况确实如此。任何源自内部的回溯NSLog
都表明它调用了NSLogv
=>_CFLogvEx
=>_CFStringCreateWithFormatAndArgumentsAux
=>_CFStringAppendFormatAndArgumentsAux
。_CFStringAppendFormatAndArgumentsAux()
(第 5365 行)是所有魔法发生的地方。您可以看到它正在手动查找所有%
替换。CFFormatObjectType
如果替换的类型是 a ,描述函数是非 nil 并且替换还没有被其他类型处理,它只会调用描述复制函数。由于我们已经证明描述没有被复制,因此可以合理地假设NSString
得到更早的处理(在这种情况下,它可能会进行原始字节复制),这让我们相信......- 正如凯文推测的那样,这里发生了堆栈错误。不知何故,指向自动释放字符串的指针被替换为另一个对象,而该对象恰好是一个
NSString
. 所以,它不会崩溃。奇怪的。但是,如果我们将静态变量的类型更改为其他类型,例如 anNSArray
,则该-description
方法确实会被调用,并且程序确实会按预期崩溃。
多么真实和完全奇怪。凯文对行为根本原因的看法最正确,而格雷厄姆纠正了我的错误想法,我对此表示赞赏。我希望我能接受两个答案...