首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Objective-C AutoreleasePool自動釋放池實現原理深度剖析

     編輯:關於C語言

Objective-C AutoreleasePool自動釋放池實現原理深度剖析

一、autorelease本質與釋放時機

autorelease = 延遲調用release。對象被加入當前AutoreleasePool,Pooldrain時統一release。

二、三種場景實驗驗證

__weak NSString *weakStr = nil;

- (void)viewDidLoad {
    [super viewDidLoad];
    // 场景1:無手動pool
    NSString *str = [NSString stringWithFormat:@"leichunfeng"];
    weakStr = str;
    NSLog(@"%@", weakStr); // 有值
    
    // 场景2:手動@autoreleasepool {}
    // @autoreleasepool {
    //     NSString *str = [NSString stringWithFormat:@"leichunfeng"];
    //     weakStr = str;
    // }
    // NSLog(@"%@", weakStr); // nil,pool結束已釋放
    
    // 场景3:變量在pool外,對象在pool內
    // NSString *str = nil;
    // @autoreleasepool { str = [NSString stringWithFormat:@"leichunfeng"]; weakStr = str; }
    // NSLog(@"%@", weakStr); // 有值(str強引用持有)
    // viewDidLoad返回後str釋放 → weakStr變nil
}

Console輸出對比:

場景viewDidLoadviewWillAppearviewDidAppear
1. 無手動pool有值有值nil
2. 手動pool內nilnilnil
3. 變量外對象內有值nilnil

三、底層實現:AutoreleasePoolPage

Runtime源碼(objc4-646)揭示:

  • 每線程維護一個指針棧,元素為對象指針或POOL_SENTINEL(哨兵,標記pool邊界)
  • 棧被劃分為雙向鏈表的AutoreleasePoolPage節點(每頁4KB)
  • 線程局部存儲(TLS)指向hotPage(最新分配頁)

頁面結構

struct AutoreleasePoolPage {
    uint32_t magic;      // 魔數校驗
    id *next;            // 下一個可用槽位
    pthread_t thread;    // 所屬線程
    AutoreleasePoolPage *parent; // 父頁
    AutoreleasePoolPage *child;  // 子頁
    uint32_t depth;      // 深度
    uint32_t hiwat;      // 高水位線
};

四、三大核心操作

1. Push(@autoreleasepool{ 入口 )

void *objc_autoreleasePoolPush() {
    return AutoreleasePoolPage::push(); // 插入POOL_SENTINEL,返回其地址(token)
}
  • 當前頁有空間 → 直接插入哨兵
  • 當前頁滿 → 分配新頁插入
  • 無頁 → 創建首頁插入

2. Autorelease(對象調用autorelease)

- (id)autorelease { return [self autorelease]; }
// 最终调用
id AutoreleasePoolPage::autorelease(id obj) {
    id *dest = autoreleaseFast(obj); // 同push邏輯插入對象指針
    return obj;
}

3. Pop(@autoreleasepool} 出口 )

void objc_autoreleasePoolPop(void *token) {
    AutoreleasePoolPage::pop(token); // 從token位置向後,所有對象release,直到哨兵
}
  • 釋放token之後所有對象
  • 將next指針回退到token位置
  • 若頁面變空且非冷頁,可釋放頁面內存

五、Clang重寫揭示@autoreleasepool本質

// Objective-C
@autoreleasepool { ... }

// Clang重寫為C++
struct __AtAutoreleasePool {
    void *token;
    __AtAutoreleasePool() { token = objc_autoreleasePoolPush(); }
    ~__AtAutoreleasePool() { objc_autoreleasePoolPop(token); }
};

{ __AtAutoreleasePool pool; ... } // RAII模式:構造push,析構pop

六、RunLoop與AutoreleasePool關系

  • 主線程RunLoop每次Event Loop迭代開始前push,結束時pop
  • 系統框架(AppKit/UIKit)自動管理,場景1即此機制
  • 子線程需手動管理(如NSThread入口函數添加pool)

七、何時需手動創建@autoreleasepool

  1. 非UI框架程序(命令行工具、daemon)
  2. 循環中創建大量臨時對象(防止內存峰值)
  3. 創建子線程(線程入口函數首行添加)

八、調試技巧:lldb watchpoint追蹤釋放時機

// 設置觀察點監控weak變量變化
(lldb) watchpoint set v weakStr
// 運行至釋放點,堆棧顯示 -[NSAutoreleasePool release] → AutoreleasePoolPage::pop
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved