對于autoreleasePool的一些思考

以下考慮均基于ARC

顧名思義 autoreleasePool 是一種自動的內存管理機制,它的工作機制很簡單,就是把需要自動管理的對象放到 autoreleasePool 中,待 autoreleasePool 結束后把池子中的對象釋放掉。

首先補充說明一點,在OC中 自生成并持有對象的方式只有 alloc/new/copy/mutableCopy 四種 ,其他方式均為非自生成并持有對象 如下代碼

    {
        //自生成并持有對象
        NSMutableArray *array1 = [[NSMutableArray alloc]init];
        
        //非自生成并持有對象, 因為array2 持有的是通過 +()array 方法返回的對象
        NSMutableArray *array2 = [NSMutableArray array];
    }

接著autoreleasePool說,正常情況下,在超出作用域時對象會被自動釋放掉,如下代碼

    {
        NSObject *obj = [[NSObject alloc]init];
        //自己生成并持有對象,引用計數 + 1 (retain)
        //retainCount = 1
    }
    //超出作用域 obj 引用計數 -1 (release)
    //此時retainCount = 0 所以 obj 銷毀

retain 和 release 成對出現,不需要另加一個自動釋放池來進行管理,一樣能完成內存的自動回收。這不就扯了嗎?本來就好好的東西,干嘛非得加個自動釋放池呢?看下面的一個例子

- (NSObject *)getObj {
    NSObject *obj = [[NSObject alloc]init];
    //自己生成并持有對象,引用計數 + 1 (retain)
    //retainCount = 1
    return obj;
    //return 導致提前出作用域 引用計數 -1 (release)
    //retainCount = 0 obj 被釋放
}

當我們需要調用這個函數賦值時 如下

    {
        NSObject *obj_1 = [self getObj];
    }

問題來了,正如剛才所說,正常情況下 出作用域時對象會被自動釋放掉,于是就造成了 obj_1 在想取得持有對象時 發現對象被釋放掉了,這顯然是不合理的。這就像是你滿心歡喜在天貓買了個冰棒,拿到快遞時發現冰棒竟然化沒了,你說鬧心不鬧心。

雖然道理是這個道理,但在實際工作時并沒有這種情況發生,這是怎么回事呢?這其實就是autoreleasePool的功勞了,編譯器會在return 之前提前把對象retain 并 注冊到自動釋放池 大體過程類似下面的代碼(這里只是用于演示過程)

- (NSObject *)getObj {
    NSObject *obj = [[NSObject alloc]init];
    //自己生成并持有對象,引用計數 + 1 (retain)
    //retainCount = 1
    
    //下面這一步由編譯器自動完成
    NSObject *autoreleaseObj = obj;
    [autoreleaseObj retain];
    //obj 引用計數 + 1
    //retainCount = 2
    
    [autoreleaseObj autorelease];
    //把autoreleaseObj注冊到自動釋放池
    
    return autoreleaseObj;
    //return 導致提前出作用域 引用計數 -1 (release)
    //retainCount = 1 obj不會被釋放
    //此時obj 被autoreleasePool持有
}

那這個pool在哪里呢?對于oc來說,整個程序都是運行在一個pool中的,可以看一下main函數的實現

int main(int argc, char * argv[]) {
    //自動釋放池
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

此時由于return后對象被autoreleasePool持有,因此不會被提前釋放掉,obj_1 自然也就可以拿到并持有該對象引用計數+1。這樣,當出作用域時obj_1被釋放,引用計數-1,當autoreleasePool退出時 對象引用計數 -1 至此被注冊到autoreleasePool的對象的引用計數 = 0 被釋放掉。由此完成了內存的自動管理。

問題是解決了,但會造成內存消耗增加,這就好比天貓的員工站出來說,為了保障你的冰棒半途不會化掉,你需要多加點錢我們給你配一個保溫箱。

為什么會增加內存消耗?由于對象會被注冊到自動釋放池,而且在自動釋放池結束前對象一直被持有,因此當大量的對象被注冊到自動釋放池時就會造成內存激增,看下面一段代碼

  - (void)viewDidLoad {
    [super viewDidLoad];

    for (int i = 0; i < 10000; i ++) {
        for (int j = 0; j < 100; j ++) {
            NSMutableArray *array = [self getArray];
            NSLog(@"%@",array);
        }
    }
 }

- (NSMutableArray *)getArray {
    NSMutableArray *arr = [[NSMutableArray alloc]init];
    [arr addObject:@"hh"];
    return arr;
}

事實上,只要是調用非自己生成并持有的對象,該對象就會被注冊到自動釋放池,比如下面的方式

NSMutableArray *arr = [NSMutableArray arrayWithObjects:@"hh", nil];
//由于調用的是非自己生成并持有對象,所以會被注冊到自動釋放池,因此在循環調用時一樣會造成內存激增。需要注意

接著內存暴增說,既然自動釋放池會造成內存暴增,那肯定要找方法來解決,我們從自動釋放池的釋放過程入手來解決這個問題,首先說一下自動釋放池的嵌套。

關于嵌套,這里補充一點,當多層自動釋放池嵌套時,內層自動釋放池會屏蔽掉外層自動釋放池對內層自動釋放池中的對象retain,很繞,說的直白一點,就是內層自動釋放池中的對象只會注冊到內層自動釋放池中。如下

//外層池子
    @autoreleasepool {
        //內層池子
        @autoreleasepool {
            NSMutableString *poolStr = [NSMutableString stringWithString:@"hh"];
            //非自己生成并持有對象,poolStr被注冊到內層池子
        }
        //內層池子結束,poolStr釋放
    }

結合上面自動釋放池內存激增的原理,既然內層池子釋放時,注冊到內層池子的對象也會被釋放,因此對于內存激增的問題,我們可以采用自動釋放池的嵌套來解決,對于上面的代碼我們稍加修改,如下

 - (void)viewDidLoad {
    [super viewDidLoad];

    for (int i = 0; i < 10000; i ++) {
        //內層池子
        @autoreleasepool {
            for (int j = 0; j < 100; j ++) {
                NSMutableArray *array = [self getArray];
                NSLog(@"%@",array);
            }
        }
    }
 }

- (NSMutableArray *)getArray {
    NSMutableArray *arr = [[NSMutableArray alloc]init];
    [arr addObject:@"hh"];
    return arr;
}

此時,由于內層池子中的一百次循環完畢后,內層池子便會結束,因此注冊到內層池子的對象便會被即時釋放,因此內存不會繼續增加。

額外補充:其實當開辟一條新的線程時,同時也會創建一個pool用于新線程的內存管理,但是POSIX由于不在ARC的內存管理范圍之內,因此通過pthread創建的新線程需要自己創建atoreleasePool,這一點可以通過打印autoreleasePool得知(打印方法,自行百度),由于class cluster 和 tagged Pointer優化 會造成部分對象的表現異常,比如NSString 和 NSNumber在內容較少時并不會生成真正的對象,因此也不會被加入到自動釋放池。

關于autoreleasePool就先整理到這吧,有不對的地方還請大神們不吝賜教。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容

  • Swift1> Swift和OC的區別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,120評論 1 32
  • 局部釋放池 創建一個新的自動釋放池的方法:ARC下: 這相當于MRC下: 其中對象s會被加入到自動釋放池,當ARC...
    thinkq閱讀 16,712評論 8 40
  • 1、[NSObject alloc]在創建完對象后,會讓該對象的retainCount+1,后續的init為初始化...
    naiyi閱讀 1,545評論 0 4
  • 29.理解引用計數 Objective-C語言使用引用計數來管理內存,也就是說,每個對象都有個可以遞增或遞減的計數...
    Code_Ninja閱讀 1,512評論 1 3
  • 面試題參考1 : 面試題[http://www.cocoachina.com/ios/20150803/12872...
    江河_ios閱讀 1,752評論 0 4