为什么我可以将消息发送到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.)
至于为什么NSArray
与NSMutableArray
不同,一个空的数组看起来确实是一个特殊的野兽:
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
。