2012-11-05 32 views
2

我剛剛注意到NSArray的一個令人驚訝的行爲,這就是爲什麼我發佈這個問題。爲什麼我可以將消息發送到NSArray的釋放實例?

我只是說像一個方法:

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

理論上這個代碼將崩潰。但在我的情況下,它從來沒有墜毀!

我改變了NSArrayNSMutableArray但這次應用程序崩潰。 爲什麼發生這種情況,爲什麼NSArray不會崩潰,NSMutableArray崩潰?

+2

空數組是ObjC中的一個特殊用例,就像字符串文字一樣。 –

+0

爲什麼不使用便利的方法,比如'NSArray * array = [NSArray array];'來設置你的數組? –

+3

@yulz如果他使用工廠方法,這個例子有什麼關係? – JustSid

回答

7

一般來說,當你釋放一個對象的時候,內存不會被清零,只要有需要的人都可以自由回收。因此,如果你保留一個指向釋放對象的指針,你通常仍然可以使用該對象一段時間(就像你對第二條消息所做的那樣)。示例代碼:

#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環境值,消息代碼開始檢查釋放對象,應在你的情況下拋出一個異常,就像你可能預期:

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

順便說一句,在默認情況下,未選中的情況下是一個內存錯誤難以調試的原因,因爲行爲可能非常不確定(這取決於內存使用模式)。您可能會在代碼周圍出現奇怪的錯誤,而錯誤是其他地方的過度釋放對象。繼續前面的例子中:

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.) 

至於爲什麼是從NSMutableArrayNSArray不同,一個空數組看起來確實是一個特殊的野獸:

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

所以這可能是與它。但是在一般情況下,使用釋放對象可能會做任何事情。它可能有效,它可能不會,它可能會泄漏你的咖啡或開始一場核戰爭。不要這樣做。

+1

喜歡結局! – Myxtic

3

我能想到的最簡單的事情是空的NSArray是基金會框架中某種「常量」 - 例如,類似於NSString文字的對象,該文字將具有retainCount(如果要調用它)-1,並且它永遠不會是-dealloc'd。

+0

一般情況是由@zoul解釋的。但在這種特殊情況下,兩種答案都是正確的! – LombaX

相關問題