为什么我可以将消息发送到NSArray的释放实例?

我只是注意到NSArray一个令人惊讶的行为,这就是为什么我发布这个问题。

我刚刚添加了一个方法,如:

 - (IBAction) crashOrNot { NSArray *array = [[NSArray alloc] init]; array = [[NSArray alloc] init]; [array release]; [array release]; } 

理论上这个代码会崩溃。 但在我的情况下,它从来没有坠毁!

我用NSMutableArray更改NSArray ,但这次应用程序崩溃。 为什么发生这种情况,为什么NSArray不崩溃和NSMutableArray崩溃?

一般来说,当你释放一个对象的时候,内存不会被清零,只要有需要的人就可以自由的回收。 因此,如果你保留一个指向释放对象的指针,通常你仍然可以使用该对象一段时间(就像你使用第二次-release消息一样)。 示例代码:

 #import <Foundation/Foundation.h> @interface Foo : NSObject @property(assign) NSUInteger canary; @end @implementation Foo @synthesize canary; @end int main(int argc, const char * argv[]) { @autoreleasepool { Foo *foo = [[Foo alloc] init]; [foo setCanary:42]; [foo release]; NSLog(@"%li", [foo canary]); // 42, no problem } return 0; } 

有没有检查这个默认情况下,行为是简单的未定义。 如果你设置了NSZombieEnabled环境值,消息传递代码开始检查释放的对象,并且应该在你的情况下抛出一个exception,就像你所预期的那样:

 *** -[Foo canary]: message sent to deallocated instance 0x100108250 

顺便说一下,缺省的未经检查的情况是内存错误难以debugging的原因之一,因为这种行为可能非常不确定(取决于内存使用模式)。 你可能会在代码中出现奇怪的错误,而这个错误是其他地方的一个过度释放的对象。 继续前面的例子:

 Foo *foo = [[Foo alloc] init]; [foo setCanary:42]; [foo release]; Foo *bar = [[Foo alloc] init]; [bar setCanary:11]; NSLog(@"%li", [foo canary]); // 11, magic! (Not guaranteed.) 

至于为什么NSArrayNSMutableArray不同,一个空的数组看起来确实是一个特殊的野兽:

 NSArray *foo = [[NSArray alloc] init]; NSArray *bar = [[NSArray alloc] init]; NSLog(@"%i", foo == bar); // yes, they point to the same object 

所以这可能与它有关。 但是在一般情况下,使用释放对象可能会做任何事情。 它可能有效,可能不会,可能会让你的咖啡溢出来,或者开始一场核战争。 不要这样做。

我能想到的最简单的事情是,一个空的NSArray在Foundation框架中是某种“常量” – 例如一个类似于NSString文字的对象,它将具有-1的retainCount (如果要调用它)它永远不会是-dealloc