在以前的一片文章咱們說了OC中謂詞操做:http://blog.csdn.net/jiangwei0910410003/article/details/41923507,從今天開始咱們就來看一下OC中最難的一部份內容:內存管理java
爲何說他難呢?由於內存若是須要咱們程序員去管理的話,那個難度確定是很大的,若是是Java,垃圾回收器會把這份工做給作了,咱們不須要關心,可是就是由於如此,Android運行速度上會慢一下,緣由很簡單,Java的垃圾回收器有不少收集算法的,這個在回收的過程當中是很浪費時間的,效率天然就低了,可是若是這份工做給程序員本身去作的話,效率上確定會增長,可是對於程序員來講任務就比較繁重了,並且還要特別的當心,千萬不能形成內存溢出和泄露。程序員
這裏咱們主要從四個方面來介紹一下內存管理算法
一、簡單的例子來了解引用計數的使用測試
二、set方法來控制引用計數問題spa
三、銷燬方法來控制引用計數問題.net
四、初始化方法來控制引用計數問題指針
下面就來簡單看一下OC中的內存管理code
這個例子有點複雜,咱們慢慢分析對象
Dog.hblog
// // Dog.h // 24_MemeryManager // // Created by jiangwei on 14-10-12. // Copyright (c) 2014年 jiangwei. All rights reserved. // #import <Foundation/Foundation.h> @interface Dog : NSObject{ NSString *_name; } - (void) setName:(NSString *)name; @end
// // Dog.m // 24_MemeryManager // // Created by jiangwei on 14-10-12. // Copyright (c) 2014年 jiangwei. All rights reserved. // #import "Dog.h" @implementation Dog - (void) setName:(NSString *)name{ _name = name; } @end
Dog類中定義了name屬性,而且給他提供了一個set方法
Person.h
// // Person.h // 24_MemeryManager // // Created by jiangwei on 14-10-12. // Copyright (c) 2014年 jiangwei. All rights reserved. // #import <Foundation/Foundation.h> @class Dog; @interface Person : NSObject{ Dog *_dog; NSString * _name; } - (id)initWithDog:(Dog*)dog; - (void)setName:(NSString *)name; - (void)setDog:(Dog *)dog; - (void)playDog; - (Dog *)dog; - (void)dealloc; @end
// // Person.m // 24_MemeryManager // // Created by jiangwei on 14-10-12. // Copyright (c) 2014年 jiangwei. All rights reserved. // #import "Person.h" @implementation Person - (id)initWithDog:(Dog*)dog{ //使用初始化的時候傳入dog self = [super init]; if(self != nil){ //由於初始化方法只會調用一次,因此這裏就沒有作判斷了 [_dog release]; _dog = [dog retain]; } return self; } - (void)setName:(NSString *)name{ //這裏name也是對象,因此也是須要進行改寫 _name = name; /* //這裏的判斷是由於setName方法可能會被屢次調用 if(_name != name){ [_name release]; [name copy];//這裏使用了copy,而沒有使用retain,這個是字符串獨有的,其餘對象類型都是使用retain的 } */ } //第一種方式 /* - (void)setDog:(Dog *)dog{ //引用計數須要+1 _dog = [dog retain]; //有時候可能須要替換Dog對象,因此這裏還要注意釋放Dog的引用 } */ //第二種方式 /* - (void)setDog:(Dog *)dog{ //使用nil去調用方法是沒有錯誤的 //可是當一個對象被銷燬的時候,指針就變成野指針了,這時候調用方法會出錯的 [_dog release]; _dog = [dog retain]; } */ //第三種方式 - (void)setDog:(Dog *)dog{ //這裏的判斷是由於setName方法可能會被屢次調用 if(_dog != dog){ [_dog release]; _dog = [dog retain]; } } - (void)playDog{ NSLog(@"playDog"); } - (Dog *)dog{ return _dog; } - (void)dealloc{ //對象類型的屬性都須要在這裏進行釋放引用 //對狗進行釋放 [_dog release]; NSLog(@"dealloc is Executing"); [super dealloc]; } @endPerson類中有一個Dog的屬性,而後提供了set方法。代碼有點複雜,咱們後面會詳細說明
下面來看一下測試代碼
main.m
// // main.m // 24_MemeryManager // // Created by jiangwei on 14-10-12. // Copyright (c) 2014年 jiangwei. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" #import "Dog.h" //內存管理 //alloc用來建立對象,建立完成以後,引用計數爲1,只調用一次 //retain使引用計數+1,能夠多調用幾回 //release使引用計數-1,能夠多調用幾回 //當引用計數爲0的時候會調用dealloc //最新的Xcode版本默認狀況下會開啓ARC機制的,當開啓這個機制以後,咱們就不能手動的顯示調用這些方法,編譯器會報錯 //因此咱們能夠將這個默認狀態的ARC關閉,可是這個只是爲了測試使用 int main(int argc, const char * argv[]) { /* Person *person = [[Person alloc] init];//引用計數爲1 NSLog(@"引用計數:%ld",[person retainCount]); //引用計數加1 [person retain]; [person release]; NSLog(@"引用計數:%ld",[person retainCount]); [person release]; */ Dog *dog = [[Dog alloc] init]; [dog setName:@"小黑"]; Dog *dog1 = [[Dog alloc] init];//引用計數爲1 [dog setName:@"大黃"]; Person *p1 = [[Person alloc] init]; [p1 setName:@"張三"]; [p1 setDog:dog]; [p1 setDog:dog1];//狗的引用替換成了大黃 Person *p2 = [[Person alloc] init]; [p2 setName:@"李四"]; [p2 setDog:dog]; //這裏引用計數爲1,這個和咱們以前說的引用計數管理有矛盾,因此咱們在使用的時候須要手動的retain NSLog(@"引用計數爲:%ld",[dog retainCount]); [dog1 release];//由於alloc的時候引用計數就爲1了 //這裏就有一個問題了,dog1對象已經被銷燬了,可是setDog對象仍是用了dog1對象調用方法了,這就會報錯了 //因此又對set方法進行改進了 [p1 setDog:dog1]; //當人銷燬的時候,還須要對狗的引用-1 //在人的dealloc方法中實現 Person *p3 = [[Person alloc] initWithDog:dog1]; [dog1 release];//dog1的引用計數:0 [p3 playDog]; [p3 release]; return 0; }
首先若是想演示這個例子的話,須要修改一下設置:
最新的XCode默認是會自動選上ARC(Automatic Reference Counting),若是咱們不把這個手動關閉的話,代碼中會報錯的。
alloc用來建立對象,建立完成以後,引用計數爲1,只調用一次
retain使引用計數+1,能夠多調用幾回
release使引用計數-1,能夠多調用幾回
當引用計數爲0的時候會調用dealloc
黃金法則:每次調用alloc一次,都須要調用release一次,他們兩是成對出現的
最新的Xcode版本默認狀況下會開啓ARC機制的,當開啓這個機制以後,咱們就不能手動的顯示調用這些方法,編譯器會報錯
因此咱們能夠將這個默認狀態的ARC關閉,可是這個只是爲了測試使用
同時咱們會發現main.m文件中沒有自動釋放池了@autoreleasepool了。
一、簡單的例子
Person *person = [[Person alloc] init];//引用計數爲1 NSLog(@"引用計數:%ld",[person retainCount]); //引用計數加1 [person retain]; [person release]; NSLog(@"引用計數:%ld",[person retainCount]); [person release];咱們建立了一個Person類,而後能夠打印一下的引用計數值:1
當咱們調用retain方法的時候,引用計數就會+1,當咱們調用release方法的時候引用計數會-1
一旦系統發現引用計數爲0的時候,就會銷燬這個對象,調用dealloc方法
二、set方法來控制引用計數
這個例子簡單吧,沒什麼問題的,如今咱們把問題複雜化
Dog *dog = [[Dog alloc] init]; [dog setName:@"小黑"]; Dog *dog1 = [[Dog alloc] init];//引用計數爲1 [dog setName:@"大黃"]; Person *p1 = [[Person alloc] init]; [p1 setName:@"張三"]; [p1 setDog:dog]; [p1 setDog:dog1];//狗的引用替換成了大黃 Person *p2 = [[Person alloc] init]; [p2 setName:@"李四"]; [p2 setDog:dog]; //這裏引用計數爲1,這個和咱們以前說的引用計數管理有矛盾,因此咱們在使用的時候須要手動的retain NSLog(@"引用計數爲:%ld",[dog retainCount]); [dog1 release];//由於alloc的時候引用計數就爲1了 //這裏就有一個問題了,dog1對象已經被銷燬了,可是setDog對象仍是用了dog1對象調用方法了,這就會報錯了 //因此又對set方法進行改進了 [p1 setDog:dog1]; //當人銷燬的時候,還須要對狗的引用-1 //在人的dealloc方法中實現咱們定義了兩條狗,而後將這兩條狗經過setDog方法設置到p1對象上,同時將第一條狗設置到p2對象上,這時候咱們打印一下第一條狗的引用計數,發現他的引用計數是1,緣由很簡單,這個1是在alloc時候有的,可是如今是有問題的,由於第一條狗被p1和p2引用者,按照正常狀況,第一條狗的引用計數爲3的。因此這時候咱們就須要修改一下Person類中setDog方法了
- (void)setDog:(Dog *)dog{ //引用計數須要+1 _dog = [dog retain]; //有時候可能須要替換Dog對象,因此這裏還要注意釋放Dog的引用 }咱們須要手動的增長dog的引用計數,這樣就正常了。
如今又有一個問題了,上面p1對象設置了第二條狗
[p1 setDog:dog1];//狗的引用替換成了大黃按照上面的setDog方法,咱們又發現一個問題,如今p1引用的狗是第二條狗了,不是第一條,那麼這時候按照正常狀況第一條狗的引用計數-1,由於他已經再也不被p1引用了。因此setDog方法還得修改:
- (void)setDog:(Dog *)dog{ //使用nil去調用方法是沒有錯誤的 //可是當一個對象被銷燬的時候,指針就變成野指針了,這時候調用方法會出錯的 [_dog release]; _dog = [dog retain]; }這時候咱們就在賦值以前,對_dog調用一次release方法,這樣就解決了上面的那個問題。
可是如今還有一個問題:
[dog1 release];//由於alloc的時候引用計數就爲1了 NSLog(@"dog1:%ld",[dog1 retainCount]); //這裏就有一個問題了,dog1對象已經被銷燬了,可是setDog對象仍是用了dog1對象調用方法了,這就會報錯了 //因此又對set方法進行改進了 [p1 setDog:dog1];在執行release代碼以後,dog1的引用計數爲1,這是繼續調用setDog方法:
- (void)setDog:(Dog *)dog{ //使用nil去調用方法是沒有錯誤的 //可是當一個對象被銷燬的時候,指針就變成野指針了,這時候調用方法會出錯的 [_dog release]; _dog = [dog retain]; }這時候_dog是dog1,繼續調用release方法,執行完以後,引用繼續爲0了,對象被釋放了,可是這時候dog參數的值仍是dog1,那麼在執行retain方法就會報錯了,這個緣由很簡單,就是咱們兩次調用了setDog方法,將dog1設置了兩次。中間調用一次release方法。因此解決這樣的問題,咱們在修改一下setDog方法:
//第三種方式 - (void)setDog:(Dog *)dog{ //這裏的判斷是由於setName方法可能會被屢次調用 if(_dog != dog){ [_dog release]; _dog = [dog retain]; } }我只須要判斷一下,以前的屬性值和當前須要設置的值是否相同。這種方式就是沒有問題了,因此咱們之後在寫set方法的時候,這就是模板了。
三、銷燬方法dealloc控制引用計數
如今假如p1被銷燬了,那麼對於dog1來講引用計數應該-1的,因此須要在Person類中的dealloc方法中修改一下:
- (void)dealloc{ //對象類型的屬性都須要在這裏進行釋放引用 //對狗進行釋放 [_dog release]; NSLog(@"dealloc is Executing"); [super dealloc]; }
四、初始化方法控制引用計數
如今還有一種狀況,若是咱們使用初始化方法來設置Dog屬性值:- (id)initWithDog:(Dog*)dog{ //使用初始化的時候傳入dog self = [super init]; if(self != nil){ //由於初始化方法只會調用一次,因此這裏就沒有作判斷了 [_dog release]; _dog = [dog retain]; } return self; }咱們這裏的處理和setDog方法的處理方式同樣,可是這裏不須要作一次判斷了,由於初始化方法只會調用一次。因此不會出現setDog的第三種狀況。
總結
這一篇主要介紹了OC中內存管理的概念和用法,後面會繼續介紹內存管理的相關知識。