iOS 中AutoreleasePool實現(xiàn)原理下

我們在分析自動釋放池底層源碼前茵臭,我們先來創(chuàng)建一個新工程,查看main函數(shù)中系統(tǒng)創(chuàng)建的自動釋放池最終轉換為底層c++代碼的情況

main函數(shù)

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
    }
    return 0;
}

我們執(zhí)行命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.mmain.m文件轉換為底層c++文件钻心,核心代碼如下:

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ {
        __AtAutoreleasePool __autoreleasepool;
    }
    return 0;
}

從上面的轉換我們可以看出,將創(chuàng)建自動釋放池的代碼@autoreleasepool{}轉換為了底層代碼__AtAutoreleasePool __autoreleasepool;

__AtAutoreleasePool __autoreleasepool;聲明了一個__AtAutoreleasePool類型的變量铅协,我們再來看看__AtAutoreleasePool的類型捷沸,我們通過在main.cpp文件中搜索,發(fā)現(xiàn)__AtAutoreleasePool為一個結構體對象狐史,源碼如下:

extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);

struct __AtAutoreleasePool {
    // 構造函數(shù)痒给,在初始化創(chuàng)建結構體的時候調用
  __AtAutoreleasePool() {
      atautoreleasepoolobj = objc_autoreleasePoolPush();
  }
    
    // 析構函數(shù)说墨,在結構體對象銷毀的時候調用
  ~__AtAutoreleasePool() {
      objc_autoreleasePoolPop(atautoreleasepoolobj);
  }
    
  void * atautoreleasepoolobj;
};

我們發(fā)現(xiàn)在__AtAutoreleasePool結構體的構造函數(shù)中調用了objc_autoreleasePoolPush()函數(shù),在析構函數(shù)中調用了objc_autoreleasePoolPop()函數(shù)

我們知道創(chuàng)建自動釋放池除了使用@autoreleasepool{}這種方式苍柏,我們還可以使用下面的面向對象的語法:

    // 創(chuàng)建自動釋放池
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    NSObject *obj = [[NSObject alloc] init];
    
    // 將對象添加到自動釋放池
    [obj autorelease];
    
    // 銷毀自動釋放池
    [pool drain];

這兩種創(chuàng)建自動釋放池方式的等價操作如圖:

image

通過使用NSAutoreleasePool的方式創(chuàng)建的自動釋放池和使用@autoreleasepool{}這種方式最終生成的結構體__AtAutoreleasePool進行對比尼斧,我們可以知道

@autoreleasepool { // 大括號作用域前調用:objc_autoreleasePoolPush()

    NSObject *obj = [[NSObject alloc] init];
    [obj autorelease];
    
} // 大括號作用域結束前調用:objc_autoreleasePoolPop()

@autoreleasepool {}方式創(chuàng)建的自動釋放池,在作用域大括號的開始前會調用objc_autoreleasePoolPush()函數(shù)试吁,在作用域大括號的結束前會調用objc_autoreleasePoolPop()函數(shù)

然后類比使用NSAutoreleasePool創(chuàng)建自動釋放池棺棵,在執(zhí)行NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]時會調用objc_autoreleasePoolPush()函數(shù),在執(zhí)行[pool drain]時會調用objc_autoreleasePoolPop()函數(shù)熄捍,等價于下面的代碼

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    /* 等同于objc_autoreleasePoolPush() */
    
    NSObject *obj = [[NSObject alloc] init];
    
    [obj autorelease];
    /* 等同于objc_autorelease(obj) */
    
    [pool drain];
    /* 等同于objc_autoreleasePoolPop(pool) */

上面我們一直提到objc_autoreleasePoolPushobjc_autoreleasePoolPop函數(shù)烛恤,接下來我們再來看看這兩個函數(shù)的具體作用,源碼查看路徑:objc4 -> NSObject.mm -> void * objc_autoreleasePoolPush(void)

源碼對這兩個函數(shù)的定義如下:

void * objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

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

通過上面的源碼我們看一看到Push和Pop函數(shù)最終都是通過AutoreleasePoolPage來調用的余耽,接下來我們再來看看AutoreleasePoolPage的源碼缚柏,源碼查看路徑:objc4 -> NSObject.mm -> AutoreleasePoolPage

通過源碼查看我們發(fā)現(xiàn)AutoreleasePoolPage是一個類,AutoreleasePoolPage類的核心源碼如下:

/***********************************************************************
   Autorelease pool implementation

   A thread's autorelease pool is a stack of pointers. 
   Each pointer is either an object to release, or POOL_BOUNDARY which is 
     an autorelease pool boundary.
   A pool token is a pointer to the POOL_BOUNDARY for that pool. When 
     the pool is popped, every object hotter than the sentinel is released.
   The stack is divided into a doubly-linked list of pages. Pages are added 
     and deleted as necessary. 
   Thread-local storage points to the hot page, where newly autoreleased 
     objects are stored. 
**********************************************************************/
class AutoreleasePoolPage 
{
     // ************** AutoreleasePoolPage類的七個成員變量 **************
     
     magic_t const magic;
    
    // 這個指針是指Page可以存放下一個autorelease對象的地址值
    id *next;
    
    // 當前所在的線程
    pthread_t const thread;
    
    // 指向上一個page對象
    AutoreleasePoolPage * const parent;
    
    // 指向下一個page對象
    AutoreleasePoolPage *child;
    
    // 自動釋放池Page的深度
    uint32_t const depth;
    uint32_t hiwat;
        
    // ************** AutoreleasePoolPage類的核心函數(shù) **************
    
    // autorelease:對象調用autorelease方法碟贾,將對象添加到自動釋放池中
public:
    static inline id autorelease(id obj)
    {
        assert(obj);
        assert(!obj->isTaggedPointer());
        
        // 通過autoreleaseFast函數(shù)币喧,找到這個對象的內(nèi)存地址
        id *dest __unused = autoreleaseFast(obj);
        
        assert(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
        
        return obj;
    }
    
    // AutoreleasePoolPage類中的 push 函數(shù)
    static inline void *push() 
    {
        id *dest;
        if (DebugPoolAllocation) {
            // Each autorelease pool starts on a new pool page.
            
            // 當沒有Page時,創(chuàng)建一個Page缕陕,將POOL_BOUNDARY放入到page中入棧粱锐,然后返回這個位置的內(nèi)存地址值
            dest = autoreleaseNewPage(POOL_BOUNDARY);
        } else {
            // 已經(jīng)有Page
            dest = autoreleaseFast(POOL_BOUNDARY);
        }
        assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
        
        // 返回值,也就是Page開始存放對象時POOL_BOUNDARY入棧時對應Page中的內(nèi)存地址
        return dest;
    }
    
    
    // AutoreleasePoolPage類中的Pop函數(shù)扛邑,這里的token就是POOL_BOUNDARY
    // pop函數(shù)會從棧底開始一直往上釋放對象怜浅,直到釋放到POOL_BOUNDARY的位置截止
    static inline void pop(void *token) 
    {
        
        AutoreleasePoolPage *page;
        id *stop;

        if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
            // Popping the top-level placeholder pool.
            
            // 判斷是否為當前的自動釋放池
            if (hotPage()) {
                // Pool was used. Pop its contents normally.
                // Pool pages remain allocated for re-use as usual.
                pop(coldPage()->begin());
            } else {
                // Pool was never used. Clear the placeholder.
                setHotPage(nil);
            }
            return;
        }

        page = pageForPointer(token);
        
        // 將token(POOL_BOUNDARY)賦值給stop指針
        stop = (id *)token;
        
        if (*stop != POOL_BOUNDARY) {
            if (stop == page->begin()  &&  !page->parent) {
                // Start of coldest page may correctly not be POOL_BOUNDARY:
                // 1. top-level pool is popped, leaving the cold page in place
                // 2. an object is autoreleased with no pool
            } else {
                // Error. For bincompat purposes this is not 
                // fatal in executables built with old SDKs.
                return badPop(token);
            }
        }

        if (PrintPoolHiwat) printHiwat();

        // 這里我們可以看到,自動釋放池釋放對象蔬崩,是通過一個終止釋放的標記來從后往前釋放池中的對象
        page->releaseUntil(stop);

        
        // memory: delete empty children
        if (DebugPoolAllocation  &&  page->empty()) {
            // special case: delete everything during page-per-pool debugging
            AutoreleasePoolPage *parent = page->parent;
            page->kill();
            setHotPage(parent);
        } else if (DebugMissingPools  &&  page->empty()  &&  !page->parent) {
            // special case: delete everything for pop(top) 
            // when debugging missing autorelease pools
            page->kill();
            setHotPage(nil);
        } 
        else if (page->child) {
            // hysteresis: keep one empty child if page is more than half full
            if (page->lessThanHalfFull()) {
                page->child->kill();
            }
            else if (page->child->child) {
                page->child->child->kill();
            }
        }
    }
}

AutoreleasePoolPage類的核心源碼可以知道恶座,當執(zhí)行objc_autoreleasePoolPush()函數(shù)時,就是調用AutoreleasePoolPage類的push函數(shù)沥阳,當執(zhí)行objc_autoreleasePoolPop()函數(shù)就是調用AutoreleasePoolPage類的pop函數(shù)跨琳,當調用對象的autorelease函數(shù)時就是調用AutoreleasePoolPage類的autorelease函數(shù)

我們從如下Autorelease pool的底層源碼注釋可知:

    Autorelease pool implementation

   A thread's autorelease pool is a stack of pointers. 
   Each pointer is either an object to release, or POOL_BOUNDARY which is 
     an autorelease pool boundary.
   A pool token is a pointer to the POOL_BOUNDARY for that pool. When 
     the pool is popped, every object hotter than the sentinel is released.
   The stack is divided into a doubly-linked list of pages. Pages are added 
     and deleted as necessary. 
   Thread-local storage points to the hot page, where newly autoreleased 
     objects are stored. 

自動釋放池是由很多個AutoreleasePoolPage組成的一個雙向鏈表的結構,并且每一個AutoreleasePoolPage中存放自動釋放的對象都是以棧的形式存儲的桐罕,如下圖所示:

image

這里有一個比較關鍵的指針POOL_BOUNDARY

例如:當我們創(chuàng)建一個自動釋放池脉让,這時就有一個POOL_BOUNDARY指針入棧存儲在AutoreleasePoolPage表中,我們可以通過下面的push函數(shù)驗證:

static inline void *push() 
{
    id *dest;
    if (DebugPoolAllocation) {
        // Each autorelease pool starts on a new pool page.
        
        // 當沒有Page時功炮,創(chuàng)建一個Page溅潜,將POOL_BOUNDARY放入到page中入棧,然后返回這個位置的內(nèi)存地址
        dest = autoreleaseNewPage(POOL_BOUNDARY);
    } else {
        // 已經(jīng)有Page
        dest = autoreleaseFast(POOL_BOUNDARY);
    }
    assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
    
    // 返回值薪伏,也就是Page開始存放對象時POOL_BOUNDARY入棧時對應Page中的內(nèi)存地址
    return dest;
}

然后我們創(chuàng)建10個對象調用autorelease方法逐一添加到自動釋放池中滚澜,autorelease函數(shù):

public:
    static inline id autorelease(id obj)
    {
        assert(obj);
        assert(!obj->isTaggedPointer());
        
        // 通過調用autoreleaseFast函數(shù),將obj對象添加至池中
        id *dest __unused = autoreleaseFast(obj);
        
        assert(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
        
        return obj;
    }

autoreleaseFast函數(shù):

static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page && !page->full()) {
            
            // 將obj對象添加到page中
            return page->add(obj);
        } else if (page) {
            return autoreleaseFullPage(obj, page);
        } else {
            return autoreleaseNoPage(obj);
        }
    }

autoreleaseFast函數(shù)中嫁怀,我們看到设捐,不管是當前page沒有滿借浊,還是當前page滿了或者是還沒有page,最終都調用了page->add(obj)語句將對象添加至池中

當需要銷毀這10個對象時萝招,這時需要調用pop函數(shù)蚂斤,源碼如下:

static inline void pop(void *token) 
    {
        
        AutoreleasePoolPage *page;
        id *stop;

        if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
            // Popping the top-level placeholder pool.
            
            // 判斷是否為當前的自動釋放池
            if (hotPage()) {
                // Pool was used. Pop its contents normally.
                // Pool pages remain allocated for re-use as usual.
                pop(coldPage()->begin());
            } else {
                // Pool was never used. Clear the placeholder.
                setHotPage(nil);
            }
            return;
        }

        page = pageForPointer(token);
        
        // 將token(POOL_BOUNDARY)賦值給stop指針
        stop = (id *)token;
        
        if (*stop != POOL_BOUNDARY) {
            if (stop == page->begin()  &&  !page->parent) {
                // Start of coldest page may correctly not be POOL_BOUNDARY:
                // 1. top-level pool is popped, leaving the cold page in place
                // 2. an object is autoreleased with no pool
            } else {
                // Error. For bincompat purposes this is not 
                // fatal in executables built with old SDKs.
                return badPop(token);
            }
        }

        if (PrintPoolHiwat) printHiwat();

        // 這里我們可以看到,自動釋放池釋放對象即寒,是通過一個終止釋放的標記來從后往前釋放池中的對象
        page->releaseUntil(stop);

        
        // memory: delete empty children
        if (DebugPoolAllocation  &&  page->empty()) {
            // special case: delete everything during page-per-pool debugging
            AutoreleasePoolPage *parent = page->parent;
            page->kill();
            setHotPage(parent);
        } else if (DebugMissingPools  &&  page->empty()  &&  !page->parent) {
            // special case: delete everything for pop(top) 
            // when debugging missing autorelease pools
            page->kill();
            setHotPage(nil);
        } 
        else if (page->child) {
            // hysteresis: keep one empty child if page is more than half full
            if (page->lessThanHalfFull()) {
                page->child->kill();
            }
            else if (page->child->child) {
                page->child->child->kill();
            }
        }
    }

從上面的源碼我們看到橡淆,調用pop函數(shù)有傳一個參數(shù)token,這個token是一個指針母赵,正是開始創(chuàng)建自動釋放池時函數(shù)的返回值逸爵,這個值就是POOL_BOUNDARY,我們從編譯出的c++文件源碼可知

struct __AtAutoreleasePool {
    // 構造函數(shù)凹嘲,在初始化創(chuàng)建結構體的時候調用
  __AtAutoreleasePool() {
        // 創(chuàng)建自動釋放池時調用objc_autoreleasePoolPush函數(shù)师倔,并返回atautoreleasepoolobj,這個就是入棧的`POOL_BOUNDARY`的地址
      atautoreleasepoolobj = objc_autoreleasePoolPush();
  }
    
    // 析構函數(shù)周蹭,在結構體對象銷毀的時候調用
  ~__AtAutoreleasePool() {
        // 銷毀自動釋放池時調用`objc_autoreleasePoolPop`函數(shù)趋艘,將`POOL_BOUNDARY`地址傳入
      objc_autoreleasePoolPop(atautoreleasepoolobj);
  }
   
   // atautoreleasepoolobj為指針類型
  void * atautoreleasepoolobj;
};

我們在來看pop函數(shù),當把token地址賦值給stop指針后凶朗,執(zhí)行了page->releaseUntil(stop)語句瓷胧,我們來看看releaseUntil函數(shù),源碼如下:

// 自動釋放池釋放對象棚愤,從后往前開始釋放搓萧,直到遇到stop地址停止釋放
void releaseUntil(id *stop) 
{
    // Not recursive: we don't want to blow out the stack 
    // if a thread accumulates a stupendous amount of garbage
    
    // 開始循環(huán)釋放對象
    while (this->next != stop) {
        // Restart from hotPage() every time, in case -release 
        // autoreleased more objects
        
        // 獲取到當前正在使用的Page,也就是熱Page
        AutoreleasePoolPage *page = hotPage();

        // fixme I think this `while` can be `if`, but I can't prove it
        while (page->empty()) {
            page = page->parent;
            setHotPage(page);
        }

        page->unprotect();
        
        // 取出`next`指針指向的對象地址
        id obj = *--page->next;
        
        memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
        page->protect();

        // 一直循環(huán)遍歷釋放對象宛畦,知道遇到POOL_BOUNDARY地址
        if (obj != POOL_BOUNDARY) {
            // 這里就是釋放對象的最終位置
            objc_release(obj);
        }
    }

    setHotPage(this);

#if DEBUG
    // we expect any children to be completely empty
    for (AutoreleasePoolPage *page = child; page; page = page->child) {
        assert(page->empty());
    }
#endif
}

releaseUntil()函數(shù)中瘸洛,我們看到了核心代碼:

    // 開始循環(huán)釋放對象
    while (this->next != stop) {
        // 一直循環(huán)遍歷釋放對象,知道遇到POOL_BOUNDARY地址
        if (obj != POOL_BOUNDARY) {
            // 這里就是釋放對象的最終位置
            objc_release(obj);
        }
    }

在while循環(huán)中判斷次和,如果地址不等于POOL_BOUNDARY反肋,就執(zhí)行objc_release()釋放對象,直到地址為POOL_BOUNDARY停止釋放踏施,到這我們應該就明白POOL_BOUNDARY的作用了

上面Autorelease pool源碼注釋我們知道石蔗,自動釋放池是由很多個AutoreleasePoolPage組成的雙向鏈表結構,那每一個AutoreleasePoolPage又分配了多大的存儲空間尼

我們通過前面的源碼static size_t const COUNT = SIZE / sizeof(id);中的SIZE的定義

#define I386_PGBYTES            4096            /* bytes per 80386 page */

從宏定義中可以看出每頁page有4096字節(jié)的大小畅形,由于AutoreleasePoolPage類中聲明了7個成員變量抓督,每個成員變量占8字節(jié),所以說每頁page還有4040(4096-56)個字節(jié)大小用來存放對象束亏,如果這一頁數(shù)據(jù)存滿了,那么就接著存放在下一頁page中阵具,以此類推

在上面的源碼分析過程我們也有看到hotPagecoldPage函數(shù)碍遍,這兩個很好理解定铜,當前正在使用的page就是hotPage,前面已經(jīng)存放滿對象的page就是coldPage怕敬,這個后面代碼打印當前自動釋放池的情況揣炕,看輸出結果就一目了然了

上面是通過Autorelease Pool底層源碼進行的分析,下面我們再來通過測試代碼來看看自動釋放池的使用情況东跪,這里由于ARCMRC使用Autorelease Pool的情況有點不同畸陡,這里我們就分開來看測試效果

我們先來看看MRC的測試情況

我們先新建一個工程,然后將Automatic Reference Counting改為NO虽填,然后創(chuàng)建一個Person類丁恭,一個Dog類,測試代碼如下:

Person

@interface Person : NSObject
{
    Dog *_dog;
}

- (void)setDog:(Dog *)dog;

- (Dog *)dog;
@end


@implementation Person

- (void)setDog:(Dog *)dog {
    if (dog != _dog) {
        [_dog release];
        dog = [dog retain];
        _dog = dog;
    }
}

- (Dog *)dog {
    return _dog;
}

- (void)dealloc {
    
    NSLog(@"%s", __func__);
    self.dog = nil;
    
    [super dealloc];
}
@end

Dog

@interface Dog : NSObject

@end


@implementation Dog

- (void)dealloc {
    [super dealloc];
    
    NSLog(@"%s", __func__);
}
@end

main函數(shù)

// _objc_autoreleasePoolPrint()函數(shù)可以打印出當前自動釋放池的使用情況斋日,在ARC和MRC下都可用
extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // 注意:當前是MRC環(huán)境
                
        NSLog(@"%@", [NSThread currentThread]);
        
        _objc_autoreleasePoolPrint();
    }
    return 0;
}

main函數(shù)中牲览,我們先來看看最簡單的情況,就是沒有任何對象添加到自動釋放池時恶守,我們使用_objc_autoreleasePoolPrint()函數(shù)來打印池中的情況第献,自動釋放池打印如下:

objc[72048]: ##############
objc[72048]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72048]: 1 releases pending.
objc[72048]: [0x103802000]  ................  PAGE  (hot) (cold)
objc[72048]: [0x103802038]  ################  POOL 0x103802038
objc[72048]: ##############

從上面的打印我們可以看出,1 releases pending兔港,表示當前池中就一個對象庸毫,這個對象就是POOL 0x103802038,也就是上面所講的POOL_BOUNDARY指針

接下來我們修改下main函數(shù)衫樊,我們創(chuàng)建對象調用autorelease添加到池中飒赃,然后創(chuàng)建對象不調用autorelease,對比看下差別橡伞,測試代碼如下:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // 注意:當前是MRC環(huán)境
                
        NSLog(@"%@", [NSThread currentThread]);
        
        _objc_autoreleasePoolPrint();
        
        NSLog(@"--------------------------------");
        
        Person *p1 = [[Person alloc] init];
        [p1 autorelease];
        
        _objc_autoreleasePoolPrint();
        
        NSLog(@"--------------------------------");
        
        Dog *d1 = [[Dog alloc] init];
        
        _objc_autoreleasePoolPrint();
    }
    return 0;
}

自動釋放池打印如下:

objc[72119]: ##############
objc[72119]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72119]: 1 releases pending.
objc[72119]: [0x100801000]  ................  PAGE  (hot) (cold)
objc[72119]: [0x100801038]  ################  POOL 0x100801038
objc[72119]: ##############
--------------------------------
objc[72119]: ##############
objc[72119]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72119]: 2 releases pending.
objc[72119]: [0x100801000]  ................  PAGE  (hot) (cold)
objc[72119]: [0x100801038]  ################  POOL 0x100801038
objc[72119]: [0x100801040]       0x10060f850  Person
objc[72119]: ##############
--------------------------------
objc[72119]: ##############
objc[72119]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72119]: 2 releases pending.
objc[72119]: [0x100801000]  ................  PAGE  (hot) (cold)
objc[72119]: [0x100801038]  ################  POOL 0x100801038
objc[72119]: [0x100801040]       0x10060f850  Person
objc[72119]: ##############

從上面的打印盒揉,我們可以看到,當我們不添加任何對象到池中時兑徘,池中就一個對象POOL_BOUNDARY刚盈,當我們創(chuàng)建一個Person對象添加到池中后,可以看到池中有了2個對象挂脑,除了最開始的POOL_BOUNDARY藕漱,還新增了0x10060f850 Person,然而當我們創(chuàng)建一個Dog對象崭闲,但是沒有調用autorelease肋联,我們發(fā)現(xiàn)Dog對象的地址沒有添加到池中。從這個對比我們知道了刁俭,要想讓對象添加到自動釋放池橄仍,對象就需要調用autorelease方法

接下來我們來看下自動釋放池嵌套使用的情況,測試代碼如下:

extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // 注意:當前是MRC環(huán)境
                
        NSLog(@"%@", [NSThread currentThread]);
        
        _objc_autoreleasePoolPrint();
        
        NSLog(@"--------------------------------");
        
        Person *p1 = [[Person alloc] init];
        [p1 autorelease];
        
        _objc_autoreleasePoolPrint();
        
        NSLog(@"--------------------------------");
        
        @autoreleasepool {
            Person *p2 = [[Person alloc] init];
            [p2 autorelease];
            
            Dog *d2 = [[Dog alloc] init];
            [d2 autorelease];
            
            _objc_autoreleasePoolPrint();
            
            NSLog(@"--------------------------------");
            
            @autoreleasepool {
                Person *p3 = [[Person alloc] init];
                [p3 autorelease];
                
                Cat *cat3 = [[Cat alloc] init];
                [cat3 autorelease];
                
                NSLog(@"--------------------------------");
                
                _objc_autoreleasePoolPrint();
            }
        }
    }
    return 0;
}

_objc_autoreleasePoolPrint()函數(shù)打印如下:

objc[72275]: ##############
objc[72275]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72275]: 1 releases pending.
objc[72275]: [0x105800000]  ................  PAGE  (hot) (cold)
objc[72275]: [0x105800038]  ################  POOL 0x105800038
objc[72275]: ##############
--------------------------------
objc[72275]: ##############
objc[72275]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72275]: 2 releases pending.
objc[72275]: [0x105800000]  ................  PAGE  (hot) (cold)
objc[72275]: [0x105800038]  ################  POOL 0x105800038
objc[72275]: [0x105800040]       0x1023057d0  Person
objc[72275]: ##############
--------------------------------
objc[72275]: ##############
objc[72275]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72275]: 5 releases pending.
objc[72275]: [0x105800000]  ................  PAGE  (hot) (cold)
objc[72275]: [0x105800038]  ################  POOL 0x105800038
objc[72275]: [0x105800040]       0x1023057d0  Person
objc[72275]: [0x105800048]  ################  POOL 0x105800048
objc[72275]: [0x105800050]       0x10073af00  Person
objc[72275]: [0x105800058]       0x10073d3b0  Dog
objc[72275]: ##############
--------------------------------
objc[72275]: ##############
objc[72275]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72275]: 8 releases pending.
objc[72275]: [0x105800000]  ................  PAGE  (hot) (cold)
objc[72275]: [0x105800038]  ################  POOL 0x105800038
objc[72275]: [0x105800040]       0x1023057d0  Person
objc[72275]: [0x105800048]  ################  POOL 0x105800048
objc[72275]: [0x105800050]       0x10073af00  Person
objc[72275]: [0x105800058]       0x10073d3b0  Dog
objc[72275]: [0x105800060]  ################  POOL 0x105800060
objc[72275]: [0x105800068]       0x10244f2f0  Person
objc[72275]: [0x105800070]       0x10244ee70  Cat
objc[72275]: ##############
--------------------------------
2020-02-19 10:48:27.081520+0800 aaaaaaa[72275:5367840] -[Cat dealloc]
2020-02-19 10:48:27.081564+0800 aaaaaaa[72275:5367840] -[Person dealloc]
2020-02-19 10:48:27.081606+0800 aaaaaaa[72275:5367840] -[Dog dealloc]
2020-02-19 10:48:27.081641+0800 aaaaaaa[72275:5367840] -[Person dealloc]
2020-02-19 10:48:27.081675+0800 aaaaaaa[72275:5367840] -[Person dealloc]

上面我們進行了3層自動釋放池的嵌套操作,我們從最后一次執(zhí)行_objc_autoreleasePoolPrint()打印可以看出侮繁,每當我們使用@autoreleasepool {}創(chuàng)建一個自動釋放池虑粥,就會有一個POOL_BOUNDARY入棧,我們從下面打印可以看出

objc[72275]: ##############
objc[72275]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72275]: 8 releases pending.
objc[72275]: [0x105800000]  ................  PAGE  (hot) (cold)
objc[72275]: [0x105800038]  ################  POOL 0x105800038
objc[72275]: [0x105800040]       0x1023057d0  Person
objc[72275]: [0x105800048]  ################  POOL 0x105800048
objc[72275]: [0x105800050]       0x10073af00  Person
objc[72275]: [0x105800058]       0x10073d3b0  Dog
objc[72275]: [0x105800060]  ################  POOL 0x105800060
objc[72275]: [0x105800068]       0x10244f2f0  Person
objc[72275]: [0x105800070]       0x10244ee70  Cat
objc[72275]: ##############

接下來我們再來看看當嵌套的自動釋放池超出了作用域會有什么結果宪哩,測試代碼如下:

extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
    
    @autoreleasepool { // 第一個自動釋放池
        // insert code here...
        
        // 注意:當前是MRC環(huán)境
                
        NSLog(@"%@", [NSThread currentThread]);
        
        Person *p1 = [[Person alloc] init];
        [p1 autorelease];
    
        @autoreleasepool { // 第二個自動釋放池
            Person *p2 = [[Person alloc] init];
            [p2 autorelease];
            
            Dog *d2 = [[Dog alloc] init];
            [d2 autorelease];
            
            @autoreleasepool { // 第三個自動釋放池
                Person *p3 = [[Person alloc] init];
                [p3 autorelease];
                
                Cat *cat3 = [[Cat alloc] init];
                [cat3 autorelease];
                                
                _objc_autoreleasePoolPrint();
                
                NSLog(@"--------------------------------");
            }
            
            NSLog(@"第三個自動釋放池超出了作用域");
            
            _objc_autoreleasePoolPrint();
            
            NSLog(@"--------------------------------");
        }
        
         NSLog(@"第二個自動釋放池超出了作用域");
        
        _objc_autoreleasePoolPrint();
        
        NSLog(@"--------------------------------");
    }
    
    NSLog(@"第一個自動釋放池超出了作用域");
    
    _objc_autoreleasePoolPrint();
    
    NSLog(@"--------------------------------");
    
    return 0;
}

終端打印結果如下:

objc[72410]: ##############
objc[72410]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72410]: 8 releases pending.
objc[72410]: [0x105803000]  ................  PAGE  (hot) (cold)
objc[72410]: [0x105803038]  ################  POOL 0x105803038
objc[72410]: [0x105803040]       0x1007045c0  Person
objc[72410]: [0x105803048]  ################  POOL 0x105803048
objc[72410]: [0x105803050]       0x100704170  Person
objc[72410]: [0x105803058]       0x100700be0  Dog
objc[72410]: [0x105803060]  ################  POOL 0x105803060
objc[72410]: [0x105803068]       0x100702760  Person
objc[72410]: [0x105803070]       0x100700dc0  Cat
objc[72410]: ##############
--------------------------------
aaaaaaa[72410:5388081] -[Cat dealloc]
aaaaaaa[72410:5388081] -[Person dealloc]
aaaaaaa[72410:5388081] 第三個自動釋放池超出了作用域
objc[72410]: ##############
objc[72410]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72410]: 5 releases pending.
objc[72410]: [0x105803000]  ................  PAGE  (hot) (cold)
objc[72410]: [0x105803038]  ################  POOL 0x105803038
objc[72410]: [0x105803040]       0x1007045c0  Person
objc[72410]: [0x105803048]  ################  POOL 0x105803048
objc[72410]: [0x105803050]       0x100704170  Person
objc[72410]: [0x105803058]       0x100700be0  Dog
objc[72410]: ##############
--------------------------------
aaaaaaa[72410:5388081] -[Dog dealloc]
aaaaaaa[72410:5388081] -[Person dealloc]
aaaaaaa[72410:5388081] 第二個自動釋放池超出了作用域
objc[72410]: ##############
objc[72410]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72410]: 2 releases pending.
objc[72410]: [0x105803000]  ................  PAGE  (hot) (cold)
objc[72410]: [0x105803038]  ################  POOL 0x105803038
objc[72410]: [0x105803040]       0x1007045c0  Person
objc[72410]: ##############
--------------------------------
aaaaaaa[72410:5388081] -[Person dealloc]
aaaaaaa[72410:5388081] 第一個自動釋放池超出了作用域
objc[72410]: ##############
objc[72410]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72410]: 0 releases pending.
objc[72410]: [0x105803000]  ................  PAGE  (hot) (cold)
objc[72410]: ##############

我們從上面的打印可以看到娩贷,當?shù)谌齻€自動釋放池超出了作用域(也就是執(zhí)行完@autoreleasepool {}的結束大括號)后,在第三個自動釋放池中創(chuàng)建的PersonCat對象就已經(jīng)銷毀了锁孟,我們從_objc_autoreleasePoolPrint()打印也可以看出彬祖,此時池中已經(jīng)沒有了第三個自動釋放池的POOL_BOUNDARY地址,和Person品抽,Cat的地址了储笑,也就是說第三個自動釋放池也銷毀了。當?shù)诙€自動釋放池超出作用域桑包,原理和第三個自動釋放池一樣南蓬。我們在看下main函數(shù)中系統(tǒng)創(chuàng)建的第一個自動釋放池超出作用域后的情況,從打印0 releases pending.可以看出哑了,此時池中已經(jīng)沒有任何對象了赘方,第一個自動釋放池超出作用域也銷毀了。這也正好驗證了上面咱們總結的結論:

當自動釋放池超出作用域弱左,則這個自動釋放池就會銷毀窄陡,當自動釋放池銷毀時,便會向池中的每一個對象發(fā)送release消息來銷毀池中的對象拆火。

接下來我們再來看看當添加到自動釋放池中的對象引用計數(shù)大于1時跳夭,當自動釋放池銷毀時,引用計數(shù)大于1的對象是進行引用計數(shù)-1還是說直接釋放對象们镜,測試代碼如下:

extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
    
    @autoreleasepool { // 第一個自動釋放池
        // insert code here...
        
        // 注意:當前是MRC環(huán)境
                
        NSLog(@"%@", [NSThread currentThread]);
        
        @autoreleasepool { // 第二個自動釋放池
            Person *p1 = [[[Person alloc] init] autorelease];
            Person *p2 = [[[Person alloc] init] autorelease];
            
            Dog *dog = [[[Dog alloc] init] autorelease]; // dog引用計數(shù)=1
            
            NSLog(@"%zd", [dog retainCount]); // 1
            
            [p1 setDog:dog]; // dog引用計數(shù)=2
            [p2 setDog:dog]; // dog引用計數(shù)=3
                    
            NSLog(@"%zd", [dog retainCount]); // 3
            
            _objc_autoreleasePoolPrint();
            
            NSLog(@"-------------------");
        }
        
        NSLog(@"---------第二個自動釋放池超出作用域---------");
        
        _objc_autoreleasePoolPrint();
    }
    
    return 0;
}

終端打印數(shù)據(jù)如下:

13:44:18.661853+0800 aaaaaaa[72892:5442548] 1
13:44:18.661890+0800 aaaaaaa[72892:5442548] 3
objc[72892]: ##############
objc[72892]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72892]: 5 releases pending.
objc[72892]: [0x100803000]  ................  PAGE  (hot) (cold)
objc[72892]: [0x100803038]  ################  POOL 0x100803038
objc[72892]: [0x100803040]  ################  POOL 0x100803040
objc[72892]: [0x100803048]       0x100622450  Person
objc[72892]: [0x100803050]       0x100620f20  Person
objc[72892]: [0x100803058]       0x100624990  Dog
objc[72892]: ##############
13:44:18.662157+0800 aaaaaaa[72892:5442548] -------------------
13:44:18.662209+0800 aaaaaaa[72892:5442548] -[Person dealloc]
13:44:18.662241+0800 aaaaaaa[72892:5442548] -[Person dealloc]
13:44:18.662280+0800 aaaaaaa[72892:5442548] -[Dog dealloc]
13:44:18.662342+0800 aaaaaaa[72892:5442548] -第二個自動釋放池超出作用域-
objc[72892]: ##############
objc[72892]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72892]: 1 releases pending.
objc[72892]: [0x100803000]  ................  PAGE  (hot) (cold)
objc[72892]: [0x100803038]  ################  POOL 0x100803038
objc[72892]: ##############

上面的測試代碼币叹,我們創(chuàng)建了一個Dog對象,然后分別讓p1p2對象持有這個dog對象模狭,這時dog對象的引用計數(shù)為3颈抚,當?shù)诙€自動釋放池超出作用域銷毀的時,我們通過打印可以看出dog對象也銷毀了嚼鹉,此時再打印自動釋放池的情況贩汉,發(fā)現(xiàn)池中就剩下第一個自動釋放池的POOL_BOUNDARY地址了,第二個自動釋放池中的所有對象全部都已釋放了

上面咱們代碼測試自動釋放池的情況一直都是使用@autoreleasepool {}方式創(chuàng)建的自動釋放池锚赤,并沒有使用NSAutoreleasePool類來創(chuàng)建自動釋放池匹舞,原理都是一樣的,這里就不在重復驗證了线脚。

接下來我們再來看看ARC環(huán)境下的自動釋放池的使用情況:

由于ARC環(huán)境中我們不能使用autoreleaseNSAutoreleasePool赐稽,所以在ARC環(huán)境中叫榕,如果想創(chuàng)建一個自動釋放池我們只能選擇使用@autoreleasepool {}這種方式創(chuàng)建,如果想將對象添加到自動釋放池中又憨,我們只能選擇使用__autoreleasing權限修飾符來修飾這個對象翠霍,測試代碼如下:

extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
    
    @autoreleasepool { // 第一個自動釋放池
        // insert code here...
        
        // 注意:當前是ARC環(huán)境
                
        NSLog(@"%@", [NSThread currentThread]);
        
        _objc_autoreleasePoolPrint();
        
        NSLog(@"---------------------------");
        
        // 沒有使用__autoreleasing修飾的對象,不會添加到自動釋放池中
        Person *p1 = [[Person alloc] init];
        
        _objc_autoreleasePoolPrint();
        
        // 使用了__autoreleasing的會添加到自動釋放池中
        __autoreleasing Person *p2 = [[Person alloc] init];
        
        _objc_autoreleasePoolPrint();
        
    }
    
    NSLog(@"-第一個自動釋放池銷毀了-");
    
    // 第一個自動釋放池超出了作用域蠢莺,自動釋放池銷毀,池中的對象也全部釋放
    _objc_autoreleasePoolPrint();
    return 0;
}

對應的終端打印如下:

objc[73081]: ##############
objc[73081]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73081]: 1 releases pending.
objc[73081]: [0x105002000]  ................  PAGE  (hot) (cold)
objc[73081]: [0x105002038]  ################  POOL 0x105002038
objc[73081]: ##############
---------------------------
objc[73081]: ##############
objc[73081]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73081]: 1 releases pending.
objc[73081]: [0x105002000]  ................  PAGE  (hot) (cold)
objc[73081]: [0x105002038]  ################  POOL 0x105002038
objc[73081]: ##############
---------------------------
objc[73081]: ##############
objc[73081]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73081]: 2 releases pending.
objc[73081]: [0x105002000]  ................  PAGE  (hot) (cold)
objc[73081]: [0x105002038]  ################  POOL 0x105002038
objc[73081]: [0x105002040]       0x103a225e0  Person
objc[73081]: ##############
---------------------------
14:18:48.329583+0800 aaaaaaa[73081:5459344] -[Person dealloc]
14:18:48.329638+0800 aaaaaaa[73081:5459344] -[Person dealloc]
14:18:48.349366+0800 aaaaaaa[73081:5459344] -第一個自動釋放池銷毀-
---------------------------
objc[73081]: ##############
objc[73081]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73081]: 0 releases pending.
objc[73081]: [0x105002000]  ................  PAGE  (hot) (cold)
objc[73081]: ##############

從上面的打印我們可以看到零如,在ARC環(huán)境中躏将,如果對象沒有被__autoreleasing修飾是不會被添加到自動釋放池中的,這個和MRCautorelease等價考蕾,當自動釋放池超出作用域祸憋,自動釋放池會銷毀,池中的所有對象也會釋放肖卧,這個和MRC是完全一樣的

接下來我們再來看看ARC環(huán)境下的自動釋放池循環(huán)嵌套情況蚯窥,測試代碼如下:

extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
    
    @autoreleasepool { // 第一個自動釋放池
        // insert code here...
        
        // 注意:當前是ARC環(huán)境
                
        NSLog(@"%@", [NSThread currentThread]);
        
        Person *p1 = [[Person alloc] init];
        
        __autoreleasing Person *p2 = [[Person alloc] init];
                
        @autoreleasepool { // 第二個自動釋放池
            
            __autoreleasing Dog *dog1 = [[Dog alloc] init];
            __autoreleasing Cat *cat1 = [[Cat alloc] init];
            
            _objc_autoreleasePoolPrint();
            
            NSLog(@"---------------------");
        }
        
        NSLog(@"-第二個自動釋放池銷毀了-");
        
        _objc_autoreleasePoolPrint();
        
        NSLog(@"---------------------");
    }
    
    NSLog(@"-第一個自動釋放池銷毀了-");
    
    _objc_autoreleasePoolPrint();
    return 0;
}

對應的終端打印如下:

objc[73141]: ##############
objc[73141]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73141]: 5 releases pending.
objc[73141]: [0x101804000]  ................  PAGE  (hot) (cold)
objc[73141]: [0x101804038]  ################  POOL 0x101804038
objc[73141]: [0x101804040]       0x10071cd70  Person
objc[73141]: [0x101804048]  ################  POOL 0x101804048
objc[73141]: [0x101804050]       0x10071c030  Dog
objc[73141]: [0x101804058]       0x10071d150  Cat
objc[73141]: ##############
---------------------
14:34:05.417828+0800 aaaaaaa[73141:5467454] -[Cat dealloc]
14:34:05.417890+0800 aaaaaaa[73141:5467454] -[Dog dealloc]
14:34:05.417976+0800 aaaaaaa[73141:5467454] -第二個自動釋放池銷毀了-
---------------------
objc[73141]: ##############
objc[73141]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73141]: 2 releases pending.
objc[73141]: [0x101804000]  ................  PAGE  (hot) (cold)
objc[73141]: [0x101804038]  ################  POOL 0x101804038
objc[73141]: [0x101804040]       0x10071cd70  Person
objc[73141]: ##############
---------------------
14:34:05.421754+0800 aaaaaaa[73141:5467454] -[Person dealloc]
14:34:05.421802+0800 aaaaaaa[73141:5467454] -[Person dealloc]
14:34:05.421872+0800 aaaaaaa[73141:5467454] -第一個自動釋放池銷毀了-
objc[73141]: ##############
objc[73141]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73141]: 0 releases pending.
objc[73141]: [0x101804000]  ................  PAGE  (hot) (cold)
objc[73141]: ##############

我們從上面的打印可以看到,在ARC環(huán)境中自動釋放池嵌套使用塞帐,當自動釋放池超出作用域時便會銷毀拦赠,池中的所有對象也都會被釋放,這和MRC是一樣的原理葵姥。


接下來我們再來探究下Autorelease Poolrunloop之間的關系荷鼠,我們創(chuàng)建一個新的iOS工程,打印當前的runloop榔幸,測試代碼如下:

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    NSLog(@"%@", [NSRunLoop currentRunLoop]);
}
@end

這里打印的信息太多允乐,我們主要關心runloopObserver的信息,核心打印如下:

observers = (
    "<CFRunLoopObserver 0x6000003143c0 [0x7fff805eff70]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff47571f14), context = <CFArray 0x600003c4f360 [0x7fff805eff70]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fcb45001038>\n)}}",
    "<CFRunLoopObserver 0x6000003100a0 [0x7fff805eff70]>{valid = Yes, activities = 0x20, repeats = Yes, order = 0, callout = _UIGestureRecognizerUpdateObserver (0x7fff4712091e), context = <CFRunLoopObserver context 0x600001900fc0>}",
    "<CFRunLoopObserver 0x600000314280 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 1999000, callout = _beforeCACommitHandler (0x7fff475a22b6), context = <CFRunLoopObserver context 0x7fcb40c00d90>}",
    "<CFRunLoopObserver 0x60000031c320 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2000000, callout = _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv (0x7fff2affcc4a), context = <CFRunLoopObserver context 0x0>}",
    "<CFRunLoopObserver 0x600000314320 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2001000, callout = _afterCACommitHandler (0x7fff475a231f), context = <CFRunLoopObserver context 0x7fcb40c00d90>}",
    "<CFRunLoopObserver 0x600000314460 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff47571f14), context = <CFArray 0x600003c4f360 [0x7fff805eff70]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fcb45001038>\n)}}"

上面的observers中削咆,我們可以看到牍疏,程序一啟動系統(tǒng)在主線程中就注冊了如下兩個Observer

// 第一個Observer
"<CFRunLoopObserver 0x6000003143c0 [0x7fff805eff70]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff47571f14), context = <CFArray 0x600003c4f360 [0x7fff805eff70]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fcb45001038>\n)}}",

// 第二個Observer
"<CFRunLoopObserver 0x600000314460 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff47571f14), context = <CFArray 0x600003c4f360 [0x7fff805eff70]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fcb45001038>\n)}}"

我們發(fā)現(xiàn)第一個Observer是用來監(jiān)聽activities = 0x1的事件,第二個Observer是用來監(jiān)聽activities = 0xa0的事件

我們再來了解下runloop對于監(jiān)聽事件狀態(tài)的枚舉:

// Run Loop Observer Activities
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),            // 1:代表是進入runloop的狀態(tài)
    kCFRunLoopBeforeTimers = (1UL << 1),     // 2:即將處理timer的狀態(tài)
    kCFRunLoopBeforeSources = (1UL << 2),    // 4:即將處理source的狀態(tài)
    kCFRunLoopBeforeWaiting = (1UL << 5),    // 32:即將進入休眠的狀態(tài)
    kCFRunLoopAfterWaiting = (1UL << 6),     // 64:已休眠拨齐,等待喚醒的狀態(tài)
    kCFRunLoopExit = (1UL << 7),             // 128:退出runloop的狀態(tài)
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

從枚舉中對應的狀態(tài)值我們便知道了鳞陨,第一個Observer是用來監(jiān)聽runloop進入事件,也就是kCFRunLoopEntry奏黑,第二個Observer對應的activities值為0xa0=160跛溉,160正好是狀態(tài)值32加上狀態(tài)值128嗓奢,也就是說第二個Observer是用來監(jiān)聽runloop即將進入休眠的狀態(tài)kCFRunLoopBeforeWaitingrunloop退出的狀態(tài)kCFRunLoopExit

我們通過上面的打印可以看到,這兩個Observer當監(jiān)聽到事件觸發(fā),都會回調執(zhí)行callout = _wrapRunLoopWithAutoreleasePoolHandler這個handler提针,從回調函數(shù)的名字_wrapRunLoopWithAutoreleasePoolHandler我們可以知道這個回調是用來處理Autorelease Pool相關操作的,那么這兩個Observer監(jiān)聽到回調后會觸發(fā)自動釋放池的什么操作尼岛都?

第一個Observer當監(jiān)聽到kCFRunLoopEntry時蚤认,這時會調用自動釋放池的objc_autoreleasePoolPush函數(shù),也就是創(chuàng)建一個自動釋放池,并且將POOL_BOUNDARY添加到自動釋放池中

第二個Observer當監(jiān)聽到kCFRunLoopBeforeWaiting時忍啸,這時會調用自動釋放池的objc_autoreleasePoolPop函數(shù)仰坦,來銷毀自動釋放池,然后再調用objc_autoreleasePoolPush函數(shù)創(chuàng)建一個自動釋放池计雌。當監(jiān)聽到kCFRunLoopExit時悄晃,這時會調用自動釋放池的objc_autoreleasePoolPop函數(shù),銷毀自動釋放池

runloop即將進入循環(huán)之前凿滤,會創(chuàng)建自動釋放池眷蚓,runloop退出循環(huán)會銷毀自動釋放池沙热。系統(tǒng)之所以這樣設計,其實也很好理解。因為在runloop即將進入循環(huán)前雁芙,系統(tǒng)創(chuàng)建了大量的事件和對象,創(chuàng)建的這些事件和對象最好能夠由自動釋放池來管理以保證得到有效的釋放洞焙,然而當runloop退出循環(huán),這時肯定也需要釋放掉之前創(chuàng)建的對象唁情,所以必然會銷毀自動釋放池

runloop即將進入休眠狀態(tài)時惦费,這時整個應用程序都進入了休眠狀態(tài),等待其它事件來喚醒runloop瞧省,所以在這時也會調用objc_autoreleasePoolPop函數(shù)來銷毀自動釋放池,以保證在休眠狀態(tài)下釋放掉無用的對象藕筋。為了保證在下一個runloop循環(huán)過程中創(chuàng)建的事件和對象都能夠及時的釋放伍掀,所以在銷毀完自動釋放池后系統(tǒng)又創(chuàng)建了一個新的自動釋放池。

我們從Autorelease Pool的官方文檔說明中也可以看出上面runloop注冊兩個Observer的作用:

The Application Kit creates an autorelease pool on the main thread at the 
beginning of every cycle of the event loop, and drains it at the end, thereby
 releasing any autoreleased objects generated while processing an event. If 
 you use the Application Kit, you therefore typically don’t have to create 
 your own pools. If your application creates a lot of temporary autoreleased 
 objects within the event loop, however, it may be beneficial to create 
 “l(fā)ocal” autorelease pools to help to minimize the peak memory footprint.

runloopAutorelease Pool的關系如圖:

image

講解示例Demo地址:

https://github.com/guangqiang-liu/11-AutoreleasePool

https://github.com/guangqiang-liu/11.1-AutoreleasePool

https://github.com/guangqiang-liu/11.2-AutoreleasePool

https://github.com/guangqiang-liu/11.3-AutoreleasePool

更多文章

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谍倦,更是在濱河造成了極大的恐慌减途,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡衫冻,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門杀狡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呜象,“玉大人,你說我怎么就攤上這事祟绊∫W” “怎么了讲坎?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長削罩。 經(jīng)常有香客問我愿阐,道長职辨,這世上最難降的妖魔是什么觉吭? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任节值,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己逻澳,他們只是感情好,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布艳狐。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪恨旱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天掂名,我揣著相機與錄音膀钠,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的也拜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼永票,長吁一口氣:“原來是場噩夢啊……” “哼岸军!你這毒婦竟也來了?” 一聲冷哼從身側響起瓦侮,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎佣谐,沒想到半個月后肚吏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡狭魂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年罚攀,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雌澄。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡斋泄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出镐牺,到底是詐尸還是另有隱情炫掐,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布睬涧,位于F島的核電站募胃,受9級特大地震影響旗唁,放射性物質發(fā)生泄漏。R本人自食惡果不足惜痹束,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一检疫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧祷嘶,春花似錦屎媳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至环壤,卻和暖如春晒来,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背郑现。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工湃崩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人接箫。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓攒读,卻偏偏與公主長得像,于是被迫代替她去往敵國和親辛友。 傳聞我的和親對象是個殘疾皇子薄扁,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355