【发布时间】:2011-06-11 10:08:48
【问题描述】:
我正在尝试将错误范围缩小到最小可重现的情况,并发现了一些奇怪的地方。
考虑这段代码:
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 方法吗?
第二个“static:”是从哪里来的?为什么这不会崩溃?
结果:
Kevin Ballard 和 Graham Lee 都是正确的。 Graham 正确地意识到 NSLog() 是 not 调用 -description (正如我错误地假设的那样),而 Kevin 几乎肯定是正确的,这是复制格式字符串和堆栈相关的奇怪问题va_list 左右。
-
NSLogging和NSString不会调用-description。 Graham 优雅地展示了这一点,如果您跟踪进行日志记录的 Core Foundation 资源,您会发现情况确实如此。任何源自NSLog内部的回溯都表明它调用了NSLogv=>_CFLogvEx=>_CFStringCreateWithFormatAndArgumentsAux=>_CFStringAppendFormatAndArgumentsAux。_CFStringAppendFormatAndArgumentsAux()(第 5365 行)是所有魔法发生的地方。您可以看到它正在手动查找所有%替换。只有当替换的类型是CFFormatObjectType、描述函数不是零并且替换还没有被其他类型处理时,它才会最终调用描述复制函数。由于我们已经证明描述没有被复制,因此可以合理地假设NSString被更早地处理(在这种情况下,它可能会进行原始字节复制),这使我们相信... - Kevin 推测,这里发生了堆栈错误。不知何故,原指向自动释放字符串的指针被替换为不同的对象,恰好是
NSString。所以,它不会崩溃。诡异的。但是,如果我们将静态变量的类型更改为其他类型,例如NSArray,则确实会调用-description方法,并且程序确实会按预期崩溃。
多么真实和完全奇怪。凯文对行为的根本原因最正确,而格雷厄姆纠正了我的错误想法。我希望我能接受两个答案...
【问题讨论】:
-
你为什么期待它崩溃?
-
NSLog([NSString stringWithFormat:@"static: %@", staticString])确实会导致崩溃,所以这确实是由于NSLog在处理%@时的不同行为所致 -
Nitpicks:我相信您的分析 wrt/“结果:”和
_CFStringAppendFormatAndArgumentsAux是不正确的。对于每个%@,_CFStringAppendFormatAndArgumentsAux将尝试 1) 通过参数传入的copyDesc函数,2)__CFCopyFormattingDescription,这对于 ObjC 对象会导致尝试_copyFormattingDescription:,最后是 3)CFCopyDescription,对于 ObjC 对象会产生_copyDescription,反汇编显示NSObject默认调用-description。因此,99% 的情况下,%@将导致-description被调用。 -
当然,对于 NON-ObjC 对象(即 Core Foundation 对象),
__CFCopyFormattingDescription和CFCopyDescription最终会执行特殊的 CF 操作。每个 CF 对象在CFRuntimeBase中都有一个用于copyFormattingDesc和copyDebugDesc的条目(对应于前面提到的函数)。对于CFString对象,这相当于调用__CFStringCopyFormattingDescription,只不过是字符串的CFStringCreateCopy。 -
因此,对于“大多数情况”,在
_CFStringAppendFormatAndArgumentsAux和%@中对于“字符串”(实际上大部分是CFString对象)相当于调用CFStringCreateCopy,结果这是CFStringAppend'ed 到正在构建的可变字符串结果。
标签: objective-c memory-management autorelease