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

我們?cè)诜治鲎詣?dòng)釋放池底層源碼前,我們先來創(chuàng)建一個(gè)新工程,查看main函數(shù)中系統(tǒng)創(chuàng)建的自動(dòng)釋放池最終轉(zhuǎn)換為底層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文件轉(zhuǎn)換為底層c++文件,核心代碼如下:

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

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

__AtAutoreleasePool __autoreleasepool;聲明了一個(gè)__AtAutoreleasePool類型的變量,我們?cè)賮砜纯?code>__AtAutoreleasePool的類型,我們通過在main.cpp文件中搜索,發(fā)現(xiàn)__AtAutoreleasePool為一個(gè)結(jié)構(gòu)體對(duì)象,源碼如下:

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

struct __AtAutoreleasePool {
    // 構(gòu)造函數(shù),在初始化創(chuàng)建結(jié)構(gòu)體的時(shí)候調(diào)用
  __AtAutoreleasePool() {
      atautoreleasepoolobj = objc_autoreleasePoolPush();
  }
    
    // 析構(gòu)函數(shù),在結(jié)構(gòu)體對(duì)象銷毀的時(shí)候調(diào)用
  ~__AtAutoreleasePool() {
      objc_autoreleasePoolPop(atautoreleasepoolobj);
  }
    
  void * atautoreleasepoolobj;
};

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

我們知道創(chuàng)建自動(dòng)釋放池除了使用@autoreleasepool{}這種方式,我們還可以使用下面的面向?qū)ο蟮恼Z法:

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

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

image

通過使用NSAutoreleasePool的方式創(chuàng)建的自動(dòng)釋放池和使用@autoreleasepool{}這種方式最終生成的結(jié)構(gòu)體__AtAutoreleasePool進(jìn)行對(duì)比,我們可以知道

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

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

@autoreleasepool {}方式創(chuàng)建的自動(dòng)釋放池,在作用域大括號(hào)的開始前會(huì)調(diào)用objc_autoreleasePoolPush()函數(shù),在作用域大括號(hào)的結(jié)束前會(huì)調(diào)用objc_autoreleasePoolPop()函數(shù)

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

    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ù),接下來我們?cè)賮砜纯催@兩個(gè)函數(shù)的具體作用,源碼查看路徑:objc4 -> NSObject.mm -> void * objc_autoreleasePoolPush(void)

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

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

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

通過上面的源碼我們看一看到Push和Pop函數(shù)最終都是通過AutoreleasePoolPage來調(diào)用的,接下來我們?cè)賮砜纯?code>AutoreleasePoolPage的源碼,源碼查看路徑:objc4 -> NSObject.mm -> AutoreleasePoolPage

通過源碼查看我們發(fā)現(xiàn)AutoreleasePoolPage是一個(gè)類,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類的七個(gè)成員變量 **************
     
     magic_t const magic;
    
    // 這個(gè)指針是指Page可以存放下一個(gè)autorelease對(duì)象的地址值
    id *next;
    
    // 當(dāng)前所在的線程
    pthread_t const thread;
    
    // 指向上一個(gè)page對(duì)象
    AutoreleasePoolPage * const parent;
    
    // 指向下一個(gè)page對(duì)象
    AutoreleasePoolPage *child;
    
    // 自動(dòng)釋放池Page的深度
    uint32_t const depth;
    uint32_t hiwat;
        
    // ************** AutoreleasePoolPage類的核心函數(shù) **************
    
    // autorelease:對(duì)象調(diào)用autorelease方法,將對(duì)象添加到自動(dòng)釋放池中
public:
    static inline id autorelease(id obj)
    {
        assert(obj);
        assert(!obj->isTaggedPointer());
        
        // 通過autoreleaseFast函數(shù),找到這個(gè)對(duì)象的內(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.
            
            // 當(dāng)沒有Page時(shí),創(chuàng)建一個(gè)Page,將POOL_BOUNDARY放入到page中入棧,然后返回這個(gè)位置的內(nèi)存地址值
            dest = autoreleaseNewPage(POOL_BOUNDARY);
        } else {
            // 已經(jīng)有Page
            dest = autoreleaseFast(POOL_BOUNDARY);
        }
        assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
        
        // 返回值,也就是Page開始存放對(duì)象時(shí)POOL_BOUNDARY入棧時(shí)對(duì)應(yīng)Page中的內(nèi)存地址
        return dest;
    }
    
    
    // AutoreleasePoolPage類中的Pop函數(shù),這里的token就是POOL_BOUNDARY
    // pop函數(shù)會(huì)從棧底開始一直往上釋放對(duì)象,直到釋放到POOL_BOUNDARY的位置截止
    static inline void pop(void *token) 
    {
        
        AutoreleasePoolPage *page;
        id *stop;

        if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
            // Popping the top-level placeholder pool.
            
            // 判斷是否為當(dāng)前的自動(dòng)釋放池
            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();

        // 這里我們可以看到,自動(dòng)釋放池釋放對(duì)象,是通過一個(gè)終止釋放的標(biāo)記來從后往前釋放池中的對(duì)象
        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類的核心源碼可以知道,當(dāng)執(zhí)行objc_autoreleasePoolPush()函數(shù)時(shí),就是調(diào)用AutoreleasePoolPage類的push函數(shù),當(dāng)執(zhí)行objc_autoreleasePoolPop()函數(shù)就是調(diào)用AutoreleasePoolPage類的pop函數(shù),當(dāng)調(diào)用對(duì)象的autorelease函數(shù)時(shí)就是調(diào)用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. 

自動(dòng)釋放池是由很多個(gè)AutoreleasePoolPage組成的一個(gè)雙向鏈表的結(jié)構(gòu),并且每一個(gè)AutoreleasePoolPage中存放自動(dòng)釋放的對(duì)象都是以棧的形式存儲(chǔ)的,如下圖所示:

image

這里有一個(gè)比較關(guān)鍵的指針POOL_BOUNDARY

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

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

然后我們創(chuàng)建10個(gè)對(duì)象調(diào)用autorelease方法逐一添加到自動(dòng)釋放池中,autorelease函數(shù):

public:
    static inline id autorelease(id obj)
    {
        assert(obj);
        assert(!obj->isTaggedPointer());
        
        // 通過調(diào)用autoreleaseFast函數(shù),將obj對(duì)象添加至池中
        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對(duì)象添加到page中
            return page->add(obj);
        } else if (page) {
            return autoreleaseFullPage(obj, page);
        } else {
            return autoreleaseNoPage(obj);
        }
    }

autoreleaseFast函數(shù)中,我們看到,不管是當(dāng)前page沒有滿,還是當(dāng)前page滿了或者是還沒有page,最終都調(diào)用了page->add(obj)語句將對(duì)象添加至池中

當(dāng)需要銷毀這10個(gè)對(duì)象時(shí),這時(shí)需要調(diào)用pop函數(shù),源碼如下:

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

        if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
            // Popping the top-level placeholder pool.
            
            // 判斷是否為當(dāng)前的自動(dòng)釋放池
            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();

        // 這里我們可以看到,自動(dòng)釋放池釋放對(duì)象,是通過一個(gè)終止釋放的標(biāo)記來從后往前釋放池中的對(duì)象
        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();
            }
        }
    }

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

struct __AtAutoreleasePool {
    // 構(gòu)造函數(shù),在初始化創(chuàng)建結(jié)構(gòu)體的時(shí)候調(diào)用
  __AtAutoreleasePool() {
        // 創(chuàng)建自動(dòng)釋放池時(shí)調(diào)用objc_autoreleasePoolPush函數(shù),并返回atautoreleasepoolobj,這個(gè)就是入棧的`POOL_BOUNDARY`的地址
      atautoreleasepoolobj = objc_autoreleasePoolPush();
  }
    
    // 析構(gòu)函數(shù),在結(jié)構(gòu)體對(duì)象銷毀的時(shí)候調(diào)用
  ~__AtAutoreleasePool() {
        // 銷毀自動(dòng)釋放池時(shí)調(diào)用`objc_autoreleasePoolPop`函數(shù),將`POOL_BOUNDARY`地址傳入
      objc_autoreleasePoolPop(atautoreleasepoolobj);
  }
   
   // atautoreleasepoolobj為指針類型
  void * atautoreleasepoolobj;
};

我們?cè)趤砜?code>pop函數(shù),當(dāng)把token地址賦值給stop指針后,執(zhí)行了page->releaseUntil(stop)語句,我們來看看releaseUntil函數(shù),源碼如下:

// 自動(dòng)釋放池釋放對(duì)象,從后往前開始釋放,直到遇到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)釋放對(duì)象
    while (this->next != stop) {
        // Restart from hotPage() every time, in case -release 
        // autoreleased more objects
        
        // 獲取到當(dāng)前正在使用的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`指針指向的對(duì)象地址
        id obj = *--page->next;
        
        memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
        page->protect();

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

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

上面Autorelease pool源碼注釋我們知道,自動(dòng)釋放池是由很多個(gè)AutoreleasePoolPage組成的雙向鏈表結(jié)構(gòu),那每一個(gè)AutoreleasePoolPage又分配了多大的存儲(chǔ)空間尼

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

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

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

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

上面是通過Autorelease Pool底層源碼進(jìn)行的分析,下面我們?cè)賮硗ㄟ^測(cè)試代碼來看看自動(dòng)釋放池的使用情況,這里由于ARCMRC使用Autorelease Pool的情況有點(diǎn)不同,這里我們就分開來看測(cè)試效果

我們先來看看MRC的測(cè)試情況

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

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ù)可以打印出當(dāng)前自動(dòng)釋放池的使用情況,在ARC和MRC下都可用
extern void _objc_autoreleasePoolPrint(void);

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

main函數(shù)中,我們先來看看最簡單的情況,就是沒有任何對(duì)象添加到自動(dòng)釋放池時(shí),我們使用_objc_autoreleasePoolPrint()函數(shù)來打印池中的情況,自動(dòng)釋放池打印如下:

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,表示當(dāng)前池中就一個(gè)對(duì)象,這個(gè)對(duì)象就是POOL 0x103802038,也就是上面所講的POOL_BOUNDARY指針

接下來我們修改下main函數(shù),我們創(chuàng)建對(duì)象調(diào)用autorelease添加到池中,然后創(chuàng)建對(duì)象不調(diào)用autorelease,對(duì)比看下差別,測(cè)試代碼如下:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // 注意:當(dāng)前是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;
}

自動(dòng)釋放池打印如下:

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]: ##############

從上面的打印,我們可以看到,當(dāng)我們不添加任何對(duì)象到池中時(shí),池中就一個(gè)對(duì)象POOL_BOUNDARY,當(dāng)我們創(chuàng)建一個(gè)Person對(duì)象添加到池中后,可以看到池中有了2個(gè)對(duì)象,除了最開始的POOL_BOUNDARY,還新增了0x10060f850 Person,然而當(dāng)我們創(chuàng)建一個(gè)Dog對(duì)象,但是沒有調(diào)用autorelease,我們發(fā)現(xiàn)Dog對(duì)象的地址沒有添加到池中。從這個(gè)對(duì)比我們知道了,要想讓對(duì)象添加到自動(dòng)釋放池,對(duì)象就需要調(diào)用autorelease方法

接下來我們來看下自動(dòng)釋放池嵌套使用的情況,測(cè)試代碼如下:

extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        // 注意:當(dāng)前是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]

上面我們進(jìn)行了3層自動(dòng)釋放池的嵌套操作,我們從最后一次執(zhí)行_objc_autoreleasePoolPrint()打印可以看出,每當(dāng)我們使用@autoreleasepool {}創(chuàng)建一個(gè)自動(dòng)釋放池,就會(huì)有一個(gè)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]: ##############

接下來我們?cè)賮砜纯串?dāng)嵌套的自動(dòng)釋放池超出了作用域會(huì)有什么結(jié)果,測(cè)試代碼如下:

extern void _objc_autoreleasePoolPrint(void);

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

終端打印結(jié)果如下:

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] 第三個(gè)自動(dòng)釋放池超出了作用域
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] 第二個(gè)自動(dòng)釋放池超出了作用域
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] 第一個(gè)自動(dòng)釋放池超出了作用域
objc[72410]: ##############
objc[72410]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72410]: 0 releases pending.
objc[72410]: [0x105803000]  ................  PAGE  (hot) (cold)
objc[72410]: ##############

我們從上面的打印可以看到,當(dāng)?shù)谌齻€(gè)自動(dòng)釋放池超出了作用域(也就是執(zhí)行完@autoreleasepool {}的結(jié)束大括號(hào))后,在第三個(gè)自動(dòng)釋放池中創(chuàng)建的PersonCat對(duì)象就已經(jīng)銷毀了,我們從_objc_autoreleasePoolPrint()打印也可以看出,此時(shí)池中已經(jīng)沒有了第三個(gè)自動(dòng)釋放池的POOL_BOUNDARY地址,和PersonCat的地址了,也就是說第三個(gè)自動(dòng)釋放池也銷毀了。當(dāng)?shù)诙€(gè)自動(dòng)釋放池超出作用域,原理和第三個(gè)自動(dòng)釋放池一樣。我們?cè)诳聪?code>main函數(shù)中系統(tǒng)創(chuàng)建的第一個(gè)自動(dòng)釋放池超出作用域后的情況,從打印0 releases pending.可以看出,此時(shí)池中已經(jīng)沒有任何對(duì)象了,第一個(gè)自動(dòng)釋放池超出作用域也銷毀了。這也正好驗(yàn)證了上面咱們總結(jié)的結(jié)論:

當(dāng)自動(dòng)釋放池超出作用域,則這個(gè)自動(dòng)釋放池就會(huì)銷毀,當(dāng)自動(dòng)釋放池銷毀時(shí),便會(huì)向池中的每一個(gè)對(duì)象發(fā)送release消息來銷毀池中的對(duì)象。

接下來我們?cè)賮砜纯串?dāng)添加到自動(dòng)釋放池中的對(duì)象引用計(jì)數(shù)大于1時(shí),當(dāng)自動(dòng)釋放池銷毀時(shí),引用計(jì)數(shù)大于1的對(duì)象是進(jìn)行引用計(jì)數(shù)-1還是說直接釋放對(duì)象,測(cè)試代碼如下:

extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
    
    @autoreleasepool { // 第一個(gè)自動(dòng)釋放池
        // insert code here...
        
        // 注意:當(dāng)前是MRC環(huán)境
                
        NSLog(@"%@", [NSThread currentThread]);
        
        @autoreleasepool { // 第二個(gè)自動(dòng)釋放池
            Person *p1 = [[[Person alloc] init] autorelease];
            Person *p2 = [[[Person alloc] init] autorelease];
            
            Dog *dog = [[[Dog alloc] init] autorelease]; // dog引用計(jì)數(shù)=1
            
            NSLog(@"%zd", [dog retainCount]); // 1
            
            [p1 setDog:dog]; // dog引用計(jì)數(shù)=2
            [p2 setDog:dog]; // dog引用計(jì)數(shù)=3
                    
            NSLog(@"%zd", [dog retainCount]); // 3
            
            _objc_autoreleasePoolPrint();
            
            NSLog(@"-------------------");
        }
        
        NSLog(@"---------第二個(gè)自動(dòng)釋放池超出作用域---------");
        
        _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] -第二個(gè)自動(dòng)釋放池超出作用域-
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]: ##############

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

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

接下來我們?cè)賮砜纯?code>ARC環(huán)境下的自動(dòng)釋放池的使用情況:

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

extern void _objc_autoreleasePoolPrint(void);

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

對(duì)應(yīng)的終端打印如下:

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] -第一個(gè)自動(dòng)釋放池銷毀-
---------------------------
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)境中,如果對(duì)象沒有被__autoreleasing修飾是不會(huì)被添加到自動(dòng)釋放池中的,這個(gè)和MRCautorelease等價(jià),當(dāng)自動(dòng)釋放池超出作用域,自動(dòng)釋放池會(huì)銷毀,池中的所有對(duì)象也會(huì)釋放,這個(gè)和MRC是完全一樣的

接下來我們?cè)賮砜纯?code>ARC環(huán)境下的自動(dòng)釋放池循環(huán)嵌套情況,測(cè)試代碼如下:

extern void _objc_autoreleasePoolPrint(void);

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

對(duì)應(yīng)的終端打印如下:

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] -第二個(gè)自動(dòng)釋放池銷毀了-
---------------------
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] -第一個(gè)自動(dòng)釋放池銷毀了-
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)境中自動(dòng)釋放池嵌套使用,當(dāng)自動(dòng)釋放池超出作用域時(shí)便會(huì)銷毀,池中的所有對(duì)象也都會(huì)被釋放,這和MRC是一樣的原理。


接下來我們?cè)賮硖骄肯?code>Autorelease Pool和runloop之間的關(guān)系,我們創(chuàng)建一個(gè)新的iOS工程,打印當(dāng)前的runloop,測(cè)試代碼如下:

@interface ViewController ()

@end

@implementation ViewController

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

這里打印的信息太多,我們主要關(guān)心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中,我們可以看到,程序一啟動(dòng)系統(tǒng)在主線程中就注冊(cè)了如下兩個(gè)Observer

// 第一個(gè)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)}}",

// 第二個(gè)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)第一個(gè)Observer是用來監(jiān)聽activities = 0x1的事件,第二個(gè)Observer是用來監(jiān)聽activities = 0xa0的事件

我們?cè)賮砹私庀?code>runloop對(duì)于監(jiān)聽事件狀態(tài)的枚舉:

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

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

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

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

第二個(gè)Observer當(dāng)監(jiān)聽到kCFRunLoopBeforeWaiting時(shí),這時(shí)會(huì)調(diào)用自動(dòng)釋放池的objc_autoreleasePoolPop函數(shù),來銷毀自動(dòng)釋放池,然后再調(diào)用objc_autoreleasePoolPush函數(shù)創(chuàng)建一個(gè)自動(dòng)釋放池。當(dāng)監(jiān)聽到kCFRunLoopExit時(shí),這時(shí)會(huì)調(diào)用自動(dòng)釋放池的objc_autoreleasePoolPop函數(shù),銷毀自動(dòng)釋放池

runloop即將進(jìn)入循環(huán)之前,會(huì)創(chuàng)建自動(dòng)釋放池,runloop退出循環(huán)會(huì)銷毀自動(dòng)釋放池。系統(tǒng)之所以這樣設(shè)計(jì),其實(shí)也很好理解。因?yàn)樵?code>runloop即將進(jìn)入循環(huán)前,系統(tǒng)創(chuàng)建了大量的事件和對(duì)象,創(chuàng)建的這些事件和對(duì)象最好能夠由自動(dòng)釋放池來管理以保證得到有效的釋放,然而當(dāng)runloop退出循環(huán),這時(shí)肯定也需要釋放掉之前創(chuàng)建的對(duì)象,所以必然會(huì)銷毀自動(dòng)釋放池

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

我們從Autorelease Pool的官方文檔說明中也可以看出上面runloop注冊(cè)兩個(gè)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的關(guān)系如圖:

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

更多文章

  • ReactNative開源項(xiàng)目OneM(1200+star):https://github.com/guangqiang-liu/OneM:歡迎小伙伴們 star
  • iOS組件化開發(fā)實(shí)戰(zhàn)項(xiàng)目(500+star):https://github.com/guangqiang-liu/iOS-Component-Pro:歡迎小伙伴們 star
  • 簡書主頁:包含多篇iOS和RN開發(fā)相關(guān)的技術(shù)文章http://www.itdecent.cn/u/023338566ca5 歡迎小伙伴們:多多關(guān)注,點(diǎn)贊
  • ReactNative QQ技術(shù)交流群(2000人):620792950 歡迎小伙伴進(jìn)群交流學(xué)習(xí)
  • iOS QQ技術(shù)交流群:678441305 歡迎小伙伴進(jìn)群交流學(xué)習(xí)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容