RunLoop和autorelease的一道面試題,runloopautorelease

來源:互聯網
上載者:User

RunLoop和autorelease的一道面試題,runloopautorelease

有這麼一道ios面試題 以下代碼有沒有什麼問題?如果有?如何解決?

for (int i = 0 ; i < largeNumber; i++) {NSString *str = [NSString stringWithFormat:@"hello -%04d",i];str = [str stringByAppendingString:@" - world"]; }
局部釋放池和RunLoop釋放池的概念:

主線程的RunLoop是預設開啟的(視圖用[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]]來停止它,也是做不到的), 每一次訊息迴圈開始的時候會先建立自動釋放池,這次迴圈結束前,會釋放自動釋放池,然後RunLoop等待下次事件來源。 在這個過程中,由RunLoop建立的釋放池類似於一個全域的釋放池。但是開發人員可以任何執行的地方建立釋放池,也就是局部的釋放池,這時的釋放池類似於代碼塊 當釋放池結束的時候會自動釋放。因此一般情況下,局部的自動釋放池很快就被釋放了,而RunLoop釋放池會等一次訊息迴圈結束的時候釋放。

什麼樣的對象會交給釋放池管理:

返回當前類的執行個體的類方法建立出來的對象,都是autorelease的,會交給所在的釋放池進行管理。 例如建立一個Person類,使用[[self alloc]init]方法建立的對象的管理不會交給它所在的釋放池,而是根據引用計數來控制釋放的時機, 如果使用[[[self alloc]init] autorelease]建立的對象,會交給所在的釋放池管理,控制其釋放的時機。

- (void)test{@autoreleasepool {        Person *p = [[Person alloc]init];        p = nil;        NSLog(@"---");    }    NSLog(@"autorelease結束");}

執行結果:

Person---dealloc---autorelease結束

 

- (void)test1{        @autoreleasepool {        Person *p = [Person person]; // 內部是[[[self alloc]init] autorelease]        p = nil;        NSLog(@"---");    }    NSLog(@"autorelease結束");}

執行的結果為:

---Person---deallocautorelease結束

因此自動釋放池被銷毀或耗盡時會向池中所有使用autorelease建立的對象發送release 訊息,釋放所有autorelease的對象,而不是所有的對象。

回到面試的問題:

當我們使用for迴圈建立很多個使用autorelease方式建立的NSString對象的時候,將所有的對象的釋放權都交給了RunLoop 的釋放池,而RunLoop的釋放池會等待這個事件處理之後才會釋放,因此就會使對象無法及時釋放,堆積在記憶體造成記憶體泄露,可以在Debug Navigation 中觀察到記憶體激增。為了驗證確實是因為autorelease這種建立方式引起的記憶體泄露,我做了如下的測試:

int largeNumber = 100000000;- (void)test3{    for (int i = 0 ; i < largeNumber; i++) {        NSString *str = [[NSString alloc]initWithFormat:@"hello -%04d",i];        str = [str stringByAppendingString:@" - world"];    }}// 這樣做的結果是記憶體幾乎沒有變化,驗證了確實是這個原因。

但是在編寫代碼的時候我們仍然習慣用類的快速建立方法,而不是alloc+init。因此解決的方案就是添加局部的釋放池,以及時釋放記憶體 如果將局部釋放池添加到迴圈外:

- (void)test4{    @autoreleasepool {        for (int i = 0 ; i < largeNumber; i++) {            NSString *str = [[NSString alloc]initWithFormat:@"hello -%04d",i];            str = [str stringByAppendingString:@" - world"];        }    }}

這樣顯然是沒有效果的,釋放池需要等迴圈執行之後再釋放記憶體,這和使用RunKLoop建立的釋放池沒有什麼區別。 較好的方案就是每次迴圈的時候添加一個釋放池:

- (void)test5{    for (int i = 0 ; i < largeNumber; i++) {        @autoreleasepool {            NSString *str = [[NSString alloc]initWithFormat:@"hello -%04d",i];            str = [str stringByAppendingString:@" - world"];        }    }}

這樣每一次迴圈的結束時都會釋放一次記憶體,因而這個迴圈全部執行完成時也幾乎補不消耗記憶體。

總結是:

做多線程開發時,需要線上程調度方法中手動添加自動釋放池,尤其是當執行迴圈的時候,如果迴圈內部有使用類的快速建立方法建立的對象, 一定要將迴圈體放到自動釋放池中。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.