为什么保留计数为0的对象会响应消息?

时间:2015-04-27 03:31:49

标签: objective-c ios8 xcode6

关闭ARC:

  1. 我使用alloc创建自定义类的实例,因此保留计数为1.

  2. 在下一行,我NSLog()实例(我在自定义类中实现了description方法)。

  3. 在下一行,我释放了该对象。

  4. 在下一行,我再次NSLog()实例,即我发送一条description消息,我在控制台中看到相同的输出。我期待某种错误输出。

  5. 以下是相关代码:

    AppDelegate.m:

    @implementation AppDelegate
    
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
        // Override point for customization after application launch.
    
        Greeter* host = [[Greeter alloc] initWithName:@"Maggie"];  //My custom class
        NSLog(@"Greeter %@", host);
        [host release];
    
        NSLog(@"Greeter %@", host); //Create error: send a `description` message to an object whose retain count is 0.
    
    
        return YES;
    }
    
    ...
    ...
    

    Greeter.m:

    ...
    
    - (NSString*)description {
        return [
            [[NSString alloc] initWithFormat:@"\n\tname: %@ \n\tCreated on %@", [self name], [self today]]
            autorelease
        ];
    }
    

    - 在控制台输出: -

    2015-04-26 21:18:58.914 Flashlight[2380:62826] Greeter 
        name: Maggie 
        Created on 2015-04-27 03:18:58 +0000
    2015-04-26 21:18:58.915 Flashlight[2380:62826] Greeter 
        name: Maggie 
        Created on 2015-04-27 03:18:58 +0000
    

    类似的代码in cocoa导致奇怪的事情发生,Xcode用EXC_BAD_ACCESS标记第二个NSLog()。

    对评论的回应:

    我决定开始一个新的应用来尝试一些事情,这就是我发现的:

    1. 在Xcode 6.2中,即使控制台中没有错误,即两个NSLog()打印相同的内容,Product>Analyze也会指出错误行:{{1 }}。如果我点击该消息,Xcode会在代码中显示一个很好的图解说明。

    2. 如果我Reference-counted object is used after it is released启用Zombie对象,那么我会收到一条错误消息,而不是第二条NSLog()消息:

    3.   

      Flashlight3 [606:11093] *** - [Greeter respondsToSelector:]:消息   发送到解除分配的实例0x7fb5e1caa8c0

      在Xcode中浏览太久之后,我想出了如何edit the scheme。在“运行”和“停止”按钮的右侧,有一个跳转栏,显示:edit the scheme,在我的情况下为ProjectName>Platform。我点击了手电筒,下拉列表显示为Flashlight>iPhone6。我点击了Edit scheme,然后在Edit scheme下面,有一个Run/Debug的复选框,我查了一下。

        

      您是否使用调试符号进行编译?

      我猜不是。我读了一些Apple文档来弄清楚如何做到这一点:我点击Project Navigator的顶行,选择Build Settings,然后我向下滚动到Build Options,然后我选择了Enable Zombie Objects,然后我点击了箭头揭示调试和发布。在Build Variants的右侧,我点击了Debug,然后输入了normal

      我不确定那是做什么的。这样做之后我没有看到任何区别。

        

      您是否在堆栈跟踪中找到了您的代码?

      在堆栈跟踪区域中:

      1. 产品与GT;简介
      2. 向下滚动窗口并选择Zombies。
      3. 然后单击红色的“录制”按钮。
      4. 将出现Zombie Messaged弹出窗口。
      5. 在弹出窗口中,单击带有灰色圆圈的箭头。
      6. 在底部窗格中,单击Zombie line。
      7. 在右下方的窗格中,在窗格顶部,选择最右侧的图标。
      8. ...列表中有一堆名称,前面有一个图标:

        enter image description here

        有些图标是黑色的。如果我点击足够的黑色图标,我最终会跳转到我的代码。但这样做是有问题的,因为当我点击其中一个黑色图标时,很难回到堆栈跟踪列表来尝试另一个黑色图标。 编辑:好的,我找到了解决这个问题的方法:当我点击“堆栈跟踪”窗格中的某个图标时,它会转到某些代码,其中一行会显示红色箭头指着它。如果我点击红色箭头,它会显示一个包含堆栈跟踪列表的弹出窗口,然后我可以点击其中一个黑色图标。

2 个答案:

答案 0 :(得分:7)

仅仅因为对象的保留计数变为0并且调用了它的dealloc方法,并不意味着对象使用的内存会立即变为垃圾。解除分配的对象可能会在内存空间重新用于其他内容之前暂时保留在内存中一段时间​​。

这似乎就是这种情况。在解除分配后调用日志host会发现解除分配对象的内存仍然完好无损,因此访问其数据会有效。

但是在Mac上的测试中似乎导致内存处理方式不同导致异常。

答案 1 :(得分:1)

rmaddy的回答是完全正确的。但是你可能想知道,在这种情况下如何获得错误信息。

有两种选择:

  • 运行静态分析器。它可以检测到其中一些错误。

  • 在方案中启用zombie选项。这将使实例对象保持活动状态并报告错误消息(如果使用它们)。