Objective c 为什么';这次坠机不是吗?

Objective c 为什么';这次坠机不是吗?,objective-c,memory-management,autorelease,Objective C,Memory Management,Autorelease,我正试图把一个bug缩小到一个最小的可重复的情况,并发现了一些奇怪的东西 考虑以下代码: static NSString *staticString = nil; int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; if (staticString == nil) { staticString = [[NSA

我正试图把一个bug缩小到一个最小的可重复的情况,并发现了一些奇怪的东西

考虑以下代码:

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_列表时,这是一个奇怪的堆栈相关问题

  • NSLogging
    NSString
    不会调用
    -description
    。格雷厄姆优雅地展示了这一点,如果你通过核心基础来进行日志记录,你会发现这是事实。任何源自
    NSLog
    内部的回溯都显示它调用
    NSLogv
    =>
    \u CFLogvEx
    =>
    \u cfstringcreatewithformat和argumentsaux
    =>
    >\u cfstringappendformat和argumentsaux
    。(第5365行)是所有魔法的所在。您可以看到它正在手动查找所有的
    %
    替换。仅当替换类型为
    CFFormatObjectType
    、描述函数为非nil且替换尚未由其他类型处理时,才会调用description copy函数。由于我们已经证明描述没有被复制,因此可以合理地假设
    NSString
    更早地得到处理(在这种情况下,它可能会进行原始字节复制),这让我们相信
  • 正如凯文推测的,这里发生了堆栈错误。不知何故,指向自动释放字符串的指针被替换为另一个对象,该对象恰好是
    NSString
    。所以,它不会崩溃。奇怪的但是,如果我们将静态变量的类型更改为其他类型,如
    NSArray
    ,则会调用
    -description
    方法,并且程序会按预期崩溃

  • 真是太奇怪了。凯文在行为的根本原因上是最正确的,格雷厄姆纠正了我的错误想法,这是值得称赞的。我希望我能接受两个答案…

    访问解除分配的内存不一定会导致崩溃。该行为未定义。你期望太高了

    这可能与@“static:”存储在与staticString相同的内存位置有关。staticString将被解除分配,它将@“static:%@”存储在回收的mem位置,因此staticString指针位于“static:%@”上,因此它最终为static:static:。

    我对您看到的最好猜测是,NSLog()复制格式字符串(可能是可变副本),然后解析参数。由于您已解除锁定了
    staticString
    ,因此格式字符串的副本恰好被放置在同一位置。这会导致您看到所描述的
    “static:static:”
    输出。当然,这个行为是未定义的——不能保证它总是使用相同的内存位置


    另一方面,您的
    NSLog(@“static:%s”,[staticString UTF8String])
    在格式字符串复制发生之前正在访问
    staticString
    ,这意味着它正在访问垃圾内存。

    您认为
    NSLog()
    调用
    实例上的
    -description
    是错误的。我刚刚添加了以下类别:

    @implementation NSString (GLDescription)
    
    - (NSString *)description {
      NSLog(@"-description called on %@", self);
      return self;
    }
    
    @end
    
    它不会导致堆栈溢出,因为它不会被递归调用。不仅如此,如果我将该类别插入到您问题中的代码中,我会发现以下输出:

    2011-01-18 23:04:11.653 LogString[3769:a0f] -description called on 1
    2011-01-18 23:04:11.656 LogString[3769:a0f] -description called on 2
    2011-01-18 23:04:11.657 LogString[3769:a0f] -description called on 3
    2011-01-18 23:04:11.658 LogString[3769:a0f] static: static: 
    
    因此,我们得出结论,
    NSLog()
    不会对在其参数中遇到的
    NSString
    调用
    -description
    。当您错误地访问发布的
    staticString
    变量时,为什么两次获取静态字符串可能是堆栈上数据的一个怪癖。

    这是“在
    free()之后使用”(
    )。发生的是“未定义的行为”。您的示例与以下示例无异:

    char *stringPtr = NULL;
    stringPtr = malloc(1024); // Example code, assumes this returns non-NULL.
    strcpy(stringPtr, "Zippers!");
    free(stringPtr);
    printf("Pants: %s\n", stringPtr);
    
    printf
    行发生了什么?谁知道呢。从<代码>裤子:拉链到<代码>裤子:(…垃圾…)堆芯转储

    所有特定于Objective-C的东西实际上都是不相关的——事实上,您使用的内存指针不再有效,这是唯一重要的事情。你最好向墙上扔飞镖,而不是试图解释“为什么”它没有崩溃并打印
    static:static
    。出于性能原因,大多数
    malloc
    实现在必要时才开始“收获”
    free()
    'd分配。这可能就是为什么你的例子没有像你期望的那样崩溃

    如果您真的想看到此特定程序崩溃,可以执行以下操作之一:

    • 将环境变量
      CFZombieLevel
      设置为
      17
      (scribble+不释放)
    • 将环境变量
      NSZombieEnabled
      设置为
      YES
    • 将环境变量
      DYLD\u INSERT\u LIBRARIES
      设置为
      /usr/lib/libgmalloc.dylib
      (请参见
      man libgmalloc

    如果在排空池之前放置日志,则它会按预期记录。当我排空时,我希望
    NSString