我写了以下代码:
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,那为什么不呢?
P.S。 我正在使用最新的XCode。
更新
NSString *string = [[NSString alloc] initWithFormat:@"test"];
NSLog(@"retainCount before = %d", [string retainCount]);// => 1
[string release];
NSLog(@"retainCount after = %d", [string retainCount]);// => 1 Why!?
答案 0 :(得分:4)
在这种情况下,框架可能会从@"test"
返回文字NSString *string = [[NSString alloc] initWithFormat:@"test"];
。也就是说,它确定可以重用文字,并在此上下文中重用它。毕竟,输入与输出匹配。
但是,您不应该依赖程序中的这些内部优化 - 只需坚持引用计数规则和明确定义的行为。
<强>更新强>
大卫的评论让我对此进行了调查。在我测试的系统上,NSString *string = [[NSString alloc] initWithFormat:@"test"];
返回 new 对象。您的程序会发送一个应该发布的对象,并且不符合永久字符串状态。
您的程序仍处于未定义的区域,并且恰好在某些情况下似乎只是作为实现细节的工件提供正确的结果 - 或者仅仅是纯粹的巧合。正如David指出的那样,在发行版和日志之间添加“stuff”会导致string
真正被破坏并可能被重用。如果您真的想知道为什么这一切都有效,您可以在执行时读取objc运行时源或爬过运行时的程序集。其中一些可能有解释(运行时实现细节),其中一些纯属巧合。
答案 1 :(得分:3)
对已发布的对象执行操作是未定义的行为。含义 - 有时你会逃脱它,有时会崩溃,有时它会在一个完全不同的地方崩溃一分钟,有时可变的十个文件会被神秘地修改。
要捕获这些问题,请使用NSZombie技术。仔细看看。那,以及一些编码规则。
这一次,你逃脱了,因为释放的内存还没有被任何东西覆盖。 string
指向的内存仍包含具有正确长度的字符串对象的字节。一段时间之后,其他东西将存在,或者内存地址将不再有效。并且不知道何时发生这种情况。
向nil
个对象发送消息是合法的。这是Objective C中定义的行为,事实上 - 没有任何反应,返回0或nil。
答案 2 :(得分:3)
确定。我很累,并没有仔细阅读你的问题。
你没有崩溃的原因是纯粹的运气。起初我虽然你使用initWithString:
,但在这种情况下,关于字符串文字的所有答案(包括我原来的答案(下面))都是有效的。
这个工作的原因只是对象被释放但你的指针仍然指向它曾经的位置,并且在你再次读取它之前不会覆盖内存。因此,当您访问从未触及的内存中读取的变量时,这意味着您将获得有效的对象。执行上述操作非常危险,将最终导致崩溃!
如果您在发行版和日志之间开始创建更多对象,那么其中一个可能会使用与您的字符串相同的内存,然后在尝试读取旧内存时会崩溃。
甚至如此脆弱,连续两次调用日志会导致崩溃。
字符串文字永远不会被释放!
请查看my answer this question,了解原因。
This answer也有很好的解释。
答案 3 :(得分:2)
一个可能的解释:你不必动态地分配字符串而不是仅仅使用常量。可能Cocoa已经知道这只是浪费内存(如果你没有创建一个可变字符串),所以它可能释放分配的对象并返回常量字符串。在一个常量字符串上,释放和保留没有效果。
为了证明这一点,值得将返回的指针与常量字符串本身进行比较:
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;
}