为什么我可以将消息发送到NSArray的解除分配的实例?

Mid*_* MP 2 memory-management nsmutablearray nsarray ios

我刚注意到一个令人惊讶的行为NSArray,这就是我发布这个问题的原因.

我刚刚添加了一个方法:

- (IBAction) crashOrNot
{
   NSArray *array = [[NSArray alloc] init];
   array = [[NSArray alloc] init];
   [array release];
   [array release];
}
Run Code Online (Sandbox Code Playgroud)

从理论上讲,这段代码会崩溃.但在我的情况下它永远不会崩溃!

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

zou*_*oul 7

通常,当您释放一个对象时,内存不会被清零,只要有需要的人就可以自由地回收它.因此,如果保留指向解除分配对象的指针,通常仍可以使用该对象一段时间(就像使用第二条-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;
}
Run Code Online (Sandbox Code Playgroud)

默认情况下没有对此进行检查,行为只是未定义.如果设置NSZombieEnabled环境值,消息传递代码将开始检查已释放的对象,并应在您的情况下抛出异常,就像您可能期望的那样:

*** -[Foo canary]: message sent to deallocated instance 0x100108250
Run Code Online (Sandbox Code Playgroud)

顺便说一句,默认的,未经检查的情况是内存错误难以调试的原因之一,因为行为可能是高度非确定性的(它取决于内存使用模式).您可能会在代码周围出现奇怪的错误,而错误是在其他地方过度释放的对象.继续上一个例子:

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.)
Run Code Online (Sandbox Code Playgroud)

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

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

所以这可能与它有关.但在一般情况下,使用解除分配的对象可能会做任何事情.它可能会起作用,它可能不会,它可能会溢出你的咖啡或开始核战争.不要这样做.