6

要么我的调试器坏了,要么我不理解一些基本的东西。

我在一个非常基本的命令行程序中有一些非常基本的代码应该崩溃。但是,它并没有崩溃。

int main (int argc, const char * argv[])
{
    NSString *string = [[NSString alloc] initWithString:@"Hello"];

    [string release];

    NSLog(@"Length: %d", [string length]);

    return 0;
}

日志语句打印“长度:5”,就像您期望的有效字符串一样。但是,到那时字符串应该被释放并且exec_bad_access应该抛出一个错误。

我已经在附加调试器和未附加调试器的情况下尝试了此代码 - 两者都给出相同的结果。我还启用了(和禁用)NSZombie,这似乎没有任何效果(我最初认为这是问题所在,因为NSZombie对象永远不会被释放 - 但它仍然不会因NSZombie禁用而崩溃)。

我在我的本地.gdbinit文件中设置了断点来中断诸如-[NSException raise]和之类的东西objc_exception_throw。我还在许多方法上设置了断点NSZombie以捕获它们。

fb -[NSException raise]
fb -[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:]
fb -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]

#define NSZombies
# this will give you help messages.  Set to NO to turn them off.
set env MallocHelp=YES
# might also be set in launch arguments.
set env NSZombieEnabled=YES
set env NSDeallocateZombies=NO
set env MallocCheckHeapEach=100000
set env MallocCheckHeapStart=100000
set env MallocScribble=YES
set env MallocGuardEdges=YES
set env MallocCheckHeapAbort=1

set env CFZombie 5

fb -[_NSZombie init]
fb -[_NSZombie retainCount]
fb -[_NSZombie retain]
fb -[_NSZombie release]
fb -[_NSZombie autorelease]
fb -[_NSZombie methodSignatureForSelector:]
fb -[_NSZombie respondsToSelector:]
fb -[_NSZombie forwardInvocation:]
fb -[_NSZombie class]
fb -[_NSZombie dealloc]

fb szone_error
fb objc_exception_throw

设置了这些断点并启用了 NSZombie,我应该会得到类似[NSString length]: message sent to deallocated instance 0x100010d39打印到控制台的内容,但我看不到这一点。我看到NSLog打印长度为5。

我在其他类(例如NSURL和)中看到了类似的行为NSNumber。但是某些类会按预期崩溃,例如NSErrorNSObject

这与类集群有关吗?他们在内存管理方面不遵循相同的规则吗?

如果类集群与这个问题无关,那么我能看到的唯一其他共同特征是不会以这种方式崩溃的类都是与 Core Foundation 对应的免费桥接的。这可能与它有关吗?

4

2 回答 2

4

retain/release是 API 和程序员之间的契约,当你遵循规则时,它不会崩溃。合同不保证如果你不遵守规则,它确实会崩溃!

在这种情况下,

[[NSString alloc] initWithString:@"Hello"]

只是返回与优化相同的对象@"Hello"。常量NSString永远不会被释放;作为一种优化,retain并且release(我认为)被忽略了。这就是它不会崩溃的原因。

@"Hello"您可以通过比较和的指针值来检查我的猜测string

于 2010-11-02T15:32:28.430 回答
1

这是一个很好的例子,说明了为什么保留计数是一个非常无用的调试工具。假设 -retain 和 -release 总是从保留计数中添加或删除 1 并且保留计数是您认为应该是的,这是错误的。

尝试

NSString *string = [[NSString alloc] initWithFormat:@"Hello %d", argc];

这应该会给你一个字符串,它的行为更像你所期望的,因为你没有从编译时间常量初始化你的字符串。但是,请注意,启用僵尸后,您将获得预期的行为,但没有僵尸,NSLog 可能会正常工作。尽管字符串已被释放,但对象中的数据仍然存在于内存中,留下一个“幽灵”,它将正确响应某些消息。

于 2010-11-02T16:01:01.273 回答