Object-C的記憶體管理和.NET有些不一樣,.NET的記憶體回收機制是使用GC自動處理回收,而Object-C本質上還是C語言,所以很多時候還是需要手動去管理記憶體回收。
1. Object-C生成一個物件
Engine *en=[[Engine alloc] init];
[en dealloc];
Object-C物件生成分配空間在堆上,需要使用指標來指向其引用。前面也說到了,Object-C中得物件其實就是C中的指標。
2. 物件初始化以及銷燬方法
+(id) alloc; 注意這裡的alloc是一個類方法,呼叫alloc方法之後會在記憶體中分配一塊空間,並且引用計數會設定為1
+(id) init; 呼叫init方法表示初始化物件
-(void) dealloc; 這裡注意一下dealloc不是一個類方法,而是一個例項方法。dealloc 方法用於銷燬物件,當引用計數為0的時候系統會自動呼叫dealloc方法銷燬物件
-(void) release; 呼叫這個方法用於釋放物件的引用,引用計數會-1
-(void) retain ;呼叫這個方法用於將引用計數+1
- (NSUInteger)retainCount; 用於獲取一個物件當前被多少物件擁有
3. 叫苦不迭的記憶體洩露
例項一:
Engine *en=[[Engine alloc] init]; Engine *en2=en; [en print]; [en dealloc]; [en2 print]; [en2 dealloc];
[en print]; 這段程式碼能夠正確的輸出資料;[en2 print] 和 [en2 dealloc] 方法則不能正常執行。因為en,en2 指標都指向了同一個物件引用,而[en dealloc]呼叫釋放了這個物件。當en2 呼叫print 和 dealloc的時候,該物件已經不存在了。
例項二:
Engine *en=[[Engine alloc] init]; Engine *en2=en; [en print]; [en release]; [en2 print]; [en2 release]; [en release];
[en print]這段程式碼能夠正常的輸出資料,而[en2 print]仍然不能正常執行。為什麼?當呼叫[[Engine alloc ] init] 的時候,en 指標指向這個物件,這個時候retainCount=1 。
Engine *en2=en 這個時候將指標en2也指向這個物件,retainCount=1;
當en呼叫release方法的時候,retainCount 數量-1,retainCount=0; 這個時候系統會自動呼叫dealloc方法,自動回收物件。所以當下面再次呼叫的時候則不能正常執行。
例項三:
Engine *en=[[Engine alloc] init]; Engine *en2=en; [en2 retain]; [en print]; [en release]; [en2 print]; [en2 release];
這段程式碼和上面一段程式碼的區別在於呼叫了[en2 retain]. 在Object-C中retainCount不會自動增加,需要呼叫retain才會增加。所以當呼叫[en2 retain]之後retainCount=2. 即使後面呼叫了[en release],retainCount仍然為1,物件不會去銷燬。所以下面會正常執行。如果不呼叫[en2 release]那麼retainCount會始終等於1,物件不會得到釋放就會發生記憶體洩露。
4. 記憶體自動回收處理
上面的處理的確有點太麻煩了,記憶體管理簡直就是噩夢。只要稍微不注意就記憶體洩露了。現在還有更好的一種方式解決以上問題,那就是autorelease pool(自動釋放物件池)。使用自動釋放物件池,在某些情況一下可以不用手工去處理物件記憶體的釋放,貌似和.NET中的垃圾回收機制有點類似了,但是我們不要完全的依賴與它,這和自動管理記憶體還是有一定差距的。
Engine *en=[[[Engine alloc] int] autorelease]; Engine *en2=en; [en2 retain]; [en print]; //[en release]; [en2 print]; [en2 release];
看到上面的程式碼,en 並沒有顯示去呼叫release方法。而autorelase pool 就是有這樣的一個好處。
5. 自動回收原理簡介
要使用自動回收我們必須手工建立自動釋放物件池,NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool內部包含一個陣列(NSMutableArray),用來儲存宣告為autorelease的所有物件。如果一個物件宣告為autorelease,系統所做的工作就是把這個物件加入到這個陣列中去。當NSAutoreleasePool自身釋放的時候,會遍歷陣列中的所有物件,並且呼叫release方法。如果物件的retainCount=0 那麼系統會釋放這些物件,如果retainCount>0,則會記憶體洩露。
在某些情況下,NSAutoreleasePool 呼叫的銷燬方法比較遲,這個時候會佔用大量的記憶體,我們也可以使用內嵌的方式,建立多個NSAutorelease的例項,讓佔用的資源立馬釋放掉。