你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS 自動釋放池ARC與MRC

iOS 自動釋放池ARC與MRC

編輯:IOS開發綜合

自動釋放池是oc提供的一種自動回收的機制,具有延遲釋放的特性,即當我們創建了一個對象,並把他加入到了自動釋放池中時,他不會立即被釋放,會等到一次runloop結束或者作用域超出{}或者超出[pool release]之後再被釋放

自動釋放池的創建與銷毀時機

MRC

1: 通過手動創建的方式來創建自動釋放池,這種方式創建的自動釋放池需要手動調用release(引用計數環境下,調用release和drain的效果相同,但是在CG下,drain會觸發GC,release不會),方法如下:
    NSAutoreleasePool *pool = [[ NSAutoreleasePool alloc]init ];//創建一個自動釋放池
    Person *person = [[Person alloc]init];
    //調autorelease方法將對象加入到自動釋放池
    //注意使用該方法,對象不會自己加入到自動釋放池,需要人為調用autorelease方法加入
    [person autorelease];
    //,手動釋放自動釋放池執行完這行代碼是,自動釋放池會對加入他中的對象做一次release操作
    [pool release];

自動釋放池銷毀時機:[pool release]代碼執行完後.

2: 通過@autoreleasepool來創建

1: 對象的創建在自動釋放池裡面

 @autoreleasepool {
        //在這個{}之內的變量默認被添加到自動釋放池
         Person *p = [[Person alloc] init];
      }//除了這個括號,p被釋放

2: 如果一個變量在自動釋放池之外創建,如下,需要通過__autoreleasing該修飾符將其加入到自動釋放池。

 @autoreleasepool {

}
Person *   __autoreleasing p = [
[Person alloc]init];
 self.person = p;

系統就是通過@autoreleasepool {}這種方式來為我們創建自動釋放池的,一個線程對應一個runloop,系統會為每一個runloop隱式的創建一個自動釋放池,所有的autoreleasePool構成一個棧式結構,在每個runloop結束時,當前棧頂的autoreleasePool會被銷毀,而且會對其中的每一個對象做一次release(嚴格來說,是你對這個對象做了幾次autorelease就會做幾次release,不一定是一次),特別指出,使用容器的block版本的枚舉器的時候,系統會自動添加一個autoreleasePool

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// 這裡被一個局部@autoreleasepool包圍著
}];

ARC

ARC下除了NSAutoreleasePool不可用以外,其他的同MRC

自動釋放池的原理

在了解自動釋放池的原理之前,我們先來看一下以下幾個概念

AutoreleasePoolPage:每一個自動釋放池沒有單獨的結構,每一個autorealeasePool對象都是由若干個autoreleasePoolPage通過雙向鏈表連接而成,類的定義如下

class AutoreleasePoolPage 
{
    magic_t const magic;
    id *next;//指向棧頂最新被添加進來的autorelease對象的下一個位置
    pthread_t const thread;//指向當前線程
    AutoreleasePoolPage * const parent;
    AutoreleasePoolPage *child;
    uint32_t const depth;
    uint32_t 
  }
autoreleasePoolPage是按照線程一一對應的, autoreleasePoolPage會開辟4096字節空間,除了上面的實例變量所占的空間,剩余的空間全部用來存儲autorelease對象的地址 id *next:指向棧頂最新被添加進來的autorelease對象的下一個位置 一個autoreleasePoolPage空間被占滿時,會創建一個新的autoreleasePoolPage對象,後來的對象添加在在新
autoreleasePoolPage中

哨兵對象:本質是一個值為nil的對象,由被定義在* NSAutoreleasePool類中的_token指針來保存。

hotPage:最新創建的AutoreleasePoolPage.

系統通過一個棧來管理所有的自動釋放池,每當創建了一個新的自動釋放池,系統就會把它壓入棧頂,並且傳入一個哨兵對象,將哨兵對象插入hotPage,這裡分三種情況

若hotPage未滿,則直接插入哨兵對象, 要是滿了,新建一個NSAutoreleasePoolPage,並將其作為hotPage,然後將哨兵對象插入 如果沒有NSAutoreleasePoolPage,則新建一個NSAutoreleasePoolPage,並將其作為hotPage,插入哨兵對象,注意。這裡的hotPage是沒有父節點的。

每當有一個自動釋放池要被釋放的時候,哨兵對象就會作為參數被傳入,找到該哨兵對象所在的位置後,將所有晚於哨兵對象的autorelease彈出,並對他們做一次release,然後將next指針一到合適的位置。

自動釋放池的應用場景

MRC:

 

對象作為函數返回值
當一個對象要作為函數返回值的時候,因為要遵循誰申請誰釋放的思想,所以應該在返回之前釋放,但要是返回之前釋放了,就會造成野指針錯誤,但是要是不釋放,那麼就違背了誰申請誰釋放的原則,所以就可以使用autorelease延遲釋放的特性,將其在返回之前做一次autorelease,加入到自動釋放池中,保證可以被返回,一次runloop之>>後系統會幫我們釋放他 臨時生成大量對象,一定要將自動釋放池放在for循環裡面,要釋放在外面,就會因為大量對象得不到及時釋放,而造成內存緊張,最後程序意外退出
for (int i = 0; i<10000; i++) {
        @autoreleasepool {
            UIImageView *imegeV = [[UIImageView alloc]init];
            imegeV.image = [UIImage imageNamed:@"efef"];
            [self.view addSubview:imegeV];
        }
    }

嵌套的autoreleasepool

@autoreleasepool {//p1被放在該自動釋放池裡面
        Person *p1 = [[Person alloc]init];
        @autoreleasepool {//p2被放在該自動釋放池裡面
            Person *p2 = [[Person alloc]init];
        }
    }

以上說明autorelease對象被添加進離他最近的自動釋放池,多層的pool會有多個哨兵對象。

ARC下autorelease的優化

ARC下,runtime提供了一套對autorelease返回值的優化策略TLS(線程局部存儲),就是為每個線程單獨分配一塊棧控件。以key-value的形式對數據進行存儲(ARC對autorelease對象的優化標記)。

1:先看優化中涉及到的幾個函數

__builtin_return_address(int level)

是一個內建函數,作用是返回函數的地址,參數是層級,如果是0,則表示是當前函數體返回地址;如果是1:則表示調用這個函數的外層函數。當我們知道了一個函數體的返回地址的時候,就可以根據反匯編,利用某些固定的偏移量,被調方可以定位到主調放在返回值後>面的匯編指令,來確定該條函數指令調用完成後下一個調用的函數

objc_autoreleaseReturnValue

通過__builtin_return_address(int level)檢測外部是不是ARC環境,可以替代autorelease,是的話直接返回object,不是的話調用objc_autorelease()將對象注冊到自動釋放池裡面,最後通過objc_retain來獲取對象。

objc_retainAutoreleasedReturnValue
通過以上代碼可以看出,

與objc_autoreleaseReturnValue配合使用,如果在執行完objc_autoreleaseReturnValue()這個函數的下一個調用函數是objc_retainAutoreleasedReturnValue,那麼就走最優化(在TLS中查詢關於這個對象,如果有,直接返回對象,不再對對象做retain)。

2:過程

新建Person類 Person.h
#import 

@interface Person : NSObject
+(instancetype)createPerson;

@end
Person.m
#import "Person.h"

@implementation Person

+(instancetype)createPerson{
    return [self new];
}

@end
ViewController.m
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
   [self testPerson];
}

- (void)TestPerson {
    self.person = [Person createPerson];
}
簡化後對應的匯編:
+ (instancetype)createPerson {
    id tmp = [self new];  
    return objc_autoreleaseReturnValue(tmp); 
} 
- (void)testFoo {
    id tmp = _objc_retainAutoreleasedReturnValue([Person createPerson]); 

}
在調用objc_autoreleaseReturnValue的時候,會先通過__builtin_return_address這個內建函數return address,然後根據這個地址判斷主調方在調用完objc_autoreleaseReturnValue這個函數以後是否緊接著調用了objc_retainAutoreleasedReturnValue函數,如果是,那麼objc_autoreleaseReturnValue()就不將返回的對象注冊到自動釋放池裡面(不做autorelease),runtime會將這個返回值存儲在TLS中,做一個優化標記,然後直接返回這個對象給函數的調用方,在外部接收這個返回值的objc_retainAutoreleasedReturnValue()會先在TLS中查詢有沒有這個對象,如果有,那麼就直接返回這個對象(不調用retain),所以通過objc_autoreleaseReturnValue和objc_retainAutoreleasedReturnValue的相互配合,利用TSL做一個中轉,在ARC下省去了autorelease和retain的步驟,在一定程度上達到了最優化.
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved