讀 Objective-C 高級編程(二)

接著上一篇的筆記,上一篇記錄了 retain,release 的實現,今天的筆記主要是從autorelease出發。

**autorelease的理解 **

定義

autorelease 顧名思義及時自動釋放的意思。它有點類似 C 語言中自動變量的特性。程序執行時,若自動變量超出其作用域,該自動變量將被自動廢棄。

接著我們看看autorelease具體的使用方法:

  • 1、生成并持有 NSAutoreleasePool 對象
  • 2、調用已分配對象的 autorelease 實例方法
  • 3、廢棄 NSAutoreleasePool 對象
// 用源代碼表示如下
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];  // 等同于 obj release

因此我們也可以這樣理解,Autorelease Pool使用場景就是需要延遲釋放某些對象的情況時,可以把他們先放到對應的Autorelease Pool中,等Autorelease Pool生命周期結束時再一起釋放。

蘋果中很多類方法用于返回 autorelease 的對象,例如像下面這兩者是相等的:

NSMutableArray *testArray = [NSMutableArray arrayWithCapacity:4];
NSMutableArray *testArray = [[NSMutableArray alloc] initWithCapacity:4];

以及

// 等同于一個局部對象,封裝了autorelease方法
NSString *testString = [NSString stringWithFormat:@"testString"];
什么時候使用

@autoreleasepool { };

根據Apple的文檔,使用場景如下:

寫基于命令行的的程序時,就是沒有UI框架,如AppKit等Cocoa框架時。
寫循環,循環里面包含了大量臨時創建的對象。
創建了新的線程。(非Cocoa程序創建線程時才需要)
長時間在后臺運行的任務。

利用@autoreleasepool優化循環的內存占用,可能是我們會用到的一點。如下面的循環,次數非常多,而且循環體里面的對象都是臨時創建使用的,就可以用@autoreleasepool包起來,讓每次循環結束時,可以及時的釋放臨時對象的內存。

 for (int i = 0; i < 10000; i++) {
     @autoreleasepool {
         // 創建對象 TODO:
          NSObject *obj = [[NSObject alloc]init];
    };
}
源碼如何實現的?

通過 objc4 庫 runtime/objc-arr.mm 來確認蘋果中的 autorelease 的實現。

class AutoreleasePoolPage
{
    static inline void *push()
    {
        // 相當于生成或持有 NSAutoreleleasePool 類對象
    }
    
    static inline void *pop (void *token)
    {
        // 相當于廢棄 NSAutoreleasePool 類對象
        releaseAll();
    }
    static inline id autorelease(id obj)
    {
        // 相當于 NSAutoreleasePool 類的 addObject 類對象
        AutoreleasePoolPage *autoreleasePoolPage  // 取到正在使用的AutoreleasePoolPage的實例
        autoreleasePoolPage->add (obj);
    }
    id *add (id obj)
    {
        // 將對象追加到內部數組中
    }
    void releaseAll()
    {
        // 調用內部數組中對象的 release 的實例方法
    }
};
void *obj_autorelreasePoolPush (void)
{
    return AutoreleasrPoolPage::push();
}

void objc_autoreleasePoolPop (void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}

id objc_autorelease (id obj)
{
    return AutoreleasePoolPage::autorelease(obj);
}

再直接點用,例如 ARC 中我們使用@autoreleasepool{}來使用一個AutoreleasePool的是時候,編譯器就將其改寫成下面的樣子:

void *context = objc_autoreleasePoolPush();
// {}中的代碼,所有接收到 autorelease 消息的對象會被添加到這個 autoreleasepool 中
objc_autoreleasePoolPop(context);

所以可以這樣理解,它實際上執行的也就是 Push 和 Pop 的操作,Push 生成,Pop 廢棄,遵循"先進后出"的原則。單個 autoreleasepool 的運行過程可以簡單地說就是 obj_autorelreasePoolPush[對象 autorelease]objc_autoreleasePoolPop這三個過程。

備注:參考下面這兩篇博文,都是非常贊的

http://blog.sunnyxx.com/2014/10/15/behind-autorelease/
http://blog.leichunfeng.com/blog/2015/05/31/objective-c-autorelease-pool-implementation-principle/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容