【问题标题】:why not EXC_BAD_ACCESS?为什么不 EXC_BAD_ACCESS?
【发布时间】:2012-06-18 21:45:54
【问题描述】:

我写了以下代码:

NSString *string = [[NSString alloc] initWithFormat:@"test"];
[string release];

NSLog(@"string lenght = %d", [string length]);
//Why I don't get EXC_BAD_ACCESS at this point?

我应该,它应该被释放。上次发布后,retainCount 应该是 0,为什么不是呢?

附: 我正在使用最新的 XCode。

更新:

NSString *string = [[NSString alloc] initWithFormat:@"test"];

NSLog(@"retainCount before = %d", [string retainCount]);// => 1

[string release];

NSLog(@"retainCount after = %d", [string retainCount]);// => 1 Why!?

【问题讨论】:

  • 是的,看起来只是运气好 :)
  • 请注意,根据其行为的定义,retainCount 永远不会返回 0。

标签: ios exc-bad-access


【解决方案1】:

在这种情况下,框架可能会从 NSString *string = [[NSString alloc] initWithFormat:@"test"]; 返回文字 @"test"。也就是说,它决定了字面量可以被重用,并在这个上下文中重用它。毕竟输入匹配输出。

但是,您不应在程序中依赖这些内部优化 - 只需坚持引用计数规则和明确定义的行为即可。

更新

大卫的评论使我对此进行了调查。在我测试的系统上,NSString *string = [[NSString alloc] initWithFormat:@"test"]; 返回一个 new 对象。您的程序向一个应该被释放的对象发送消息,并且不符合不朽字符串状态的条件。

您的程序仍然落入未定义的领域,并且在某些情况下似乎给出了正确的结果,这只是作为实现细节的产物——或者纯粹是巧合。正如 David 指出的那样,在发布和日志之间添加“东西”可能会导致 string 真正被破坏并可能被重用。如果您真的想知道为什么这一切都有效,您可以阅读 objc 运行时源或在运行时的程序集执行时抓取它。有的可能有解释(运行时实现细节),有的纯属巧合。

【讨论】:

  • 我很确定情况并非如此......对象真的消失了,但内存仍然没有被触及,所以指针指向内存中的一个有效对象,这就是为什么它有效.在两者之间放置其他调用会破坏它。
  • @DavidRönnqvist 谢谢 - 你是对的。 -[NSString initWithFormat:] 没有在我测试的系统上进行此优化。
【解决方案2】:

更新:

好的。我很累,没有仔细阅读你的问题。

您没有崩溃的原因是纯属运气。一开始我以为您使用的是initWithString:,在这种情况下,关于字符串文字的所有答案(包括我原来的答案(如下))都是有效的。


我所说的“纯粹的运气”是什么意思

这样做的原因只是对象已被释放,但您的指针仍指向它以前所在的位置,并且在您再次读取之前内存没有被覆盖。因此,当您访问从未触及的内存中读取的变量时,这意味着您获得了一个有效的对象。这样做是非常危险的,并且在未来最终导致崩溃!

如果您在发布和日志之间开始创建更多对象,那么其中一个可能会使用与您的字符串相同的内存,然后您在尝试读取旧内存时会崩溃。

竟然这么脆弱,连续两次调用log都会导致crash。


原答案:

字符串字面量永远不会被释放!

查看my answer for this question 了解为什么会这样。

This answer也有很好的解释。

【讨论】:

    【解决方案3】:

    对已释放对象执行操作是未定义的行为。含义 - 有时你侥幸逃脱,有时它崩溃,有时它在一分钟后在完全不同的地方崩溃,有时十个文件之外的变量被神秘地修改了。

    要发现这些问题,请使用 NSZombie 技术。查一下。那,还有一些编码纪律。

    这一次,你逃脱了,因为释放的内存还没有被任何东西覆盖。 string 指向的内存仍然包含具有正确长度的字符串对象的字节。一段时间后,会出现其他东西,或者内存地址将不再有效。不知道何时会发生这种情况。

    但是,向nil 对象发送消息是合法的。实际上,这是 Objective C 中定义的行为 - 没有任何反应,返回 0 或 nil。

    【讨论】:

    • 这不能回答 OP 的问题。请参阅我和贾斯汀的回答。
    • @H2CO3:你在猜测 Cocoa 的内部结构,没有证据,也没有文档支持你。我对 OP 问题的回答是“这次你侥幸逃脱了,但你不会总是这样”。
    • 但他寻找解释为什么会发生这种情况,而不是寻找良好的编程实践。顺便说一句,你注意到我以To prove this, ...开头的句子了吗?
    • 您是否在设备上运行证明?你的理论成立吗?而且 - 更重要的是 - 你能确定它适用于每个版本的 iOS 吗?顺便说一句,OP 使用 [initWithFormat],而不是 [initWithString]。
    • 巧合。这是正确的答案。虽然人们可能会猜测实现细节,但这无关紧要(尽管可能很有趣)。
    【解决方案4】:

    一种可能的解释:您多余地动态分配字符串,而不仅仅是使用常量。可能 Cocoa 已经知道这只是浪费内存(如果您没有创建可变字符串),因此它可能会释放分配的对象并返回常量字符串。而对于一个常量字符串,release和retain没有任何作用。

    为了证明这一点,值得将返回的指针与常量字符串本身进行比较:

    int main()
    {
        NSString *s = @"Hello World!";
        NSString *t = [[NSString alloc] initWithFormat:s];
    
        if (s == t)
            NSLog(@"Strings are the same");
        else
            NSLog(@"Not the same; another instance was allocated");
    
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-21
      • 1970-01-01
      相关资源
      最近更新 更多