AutoReleasePool 工作机制
背景:
相比很多人都有这个困惑,@autoreleasepool 到底做了什么,今天我们就从源码的基础上去分析,彻底揭开它神秘面纱
源码路径:
https://opensource.apple.com/tarballs/objc4/
得益于objc 开源,我们可以一窥其本质,在接下来分析中我会尽可能引入源码
@autoreleasepool 转成 c++ 到底是什么样的
Objective-C @autoreleasepool { NSObject *pObjecdt = nil ; pObjecdt = [[Test alloc] init]; [pObjecdt autorelease]; NSLog(@"refcount = %d", CFGetRetainCount(pObjecdt)); }
OC 转 C++
使用命令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
就可以把对对应m 转成c++
Objective-C { __AtAutoreleasePool __autoreleasepool; NSObject *pObjecdt = __null; pObjecdt = ((Test *(*)(id, SEL))(void *)objc_msgSend)((id)((Test *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Test"), sel_registerName("alloc")), sel_registerName("init")); ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)pObjecdt, sel_registerName("autorelease")); NSLog((NSString *)&__NSConstantStringImpl__var_folders_x4_q4k0pbd17n90b9x6_v_h_rb40000gn_T_main_092efe_mi_0, CFGetRetainCount(pObjecdt)); }
也就是说@autoreleasepool 转换成了 __AtAutoreleasePool __autoreleasepool;
下边就介绍__AtAutoreleasePool 的定义
__AtAutoreleasePool
C++ struct __AtAutoreleasePool { __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();} ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);} void * atautoreleasepoolobj; };
通过上边可以看的出来 这是很简单的结构图,遵循的RAII的原则,也就是说构造的时候生成atautoreleasepoolobj 指针,而 西沟的时候把atautoreleasepoolobj 传入到objc_autoreleasePoolPop中去,所以要搞清楚AutoReleasePool 就要搞清楚objc_autoreleasePoolPush 与 objc_autoreleasePoolPop 是干啥的。
objc_autoreleasePoolPush
C++ struct AutoreleasePoolPageData { magic_t const magic; __unsafe_unretained id *next; pthread_t const thread; AutoreleasePoolPage * const parent; AutoreleasePoolPage *child; uint32_t const depth; uint32_t hiwat; };class AutoreleasePoolPage : private AutoreleasePoolPageData { }void * objc_autoreleasePoolPush(void ) { return AutoreleasePoolPage::push(); } # define POOL_BOUNDARY nil static inline void *push() { id *dest; dest = autoreleaseFast(POOL_BOUNDARY); return dest; } //obj = POOL_BOUNDARY static inline id *autoreleaseFast(id obj) { AutoreleasePoolPage *page = hotPage (); if (page && !page->full()) { return page->add (obj); } else if (page) { return autoreleaseFullPage(obj, page); } else { return autoreleaseNoPage(obj); } } static inline AutoreleasePoolPage *hotPage () { AutoreleasePoolPage *result = (AutoreleasePoolPage *) tls_get_direct(key); if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil ; if (result) result->fastcheck(); return result; } class AutoreleasePoolPage { //obj = POOL_BOUNDARY id *add (id obj) { id *ret; ret = next; // faster than `return next-1` because of aliasing *next++ = obj; return ret; } }
通过上边调用关系可以看的出来,AutoreleasePoolPage 是跟线程相关的,push首先从tls 中取出当前线程相关连的AutoreleasePoolPage指针,类似于opengl context的原理,把POOL_BOUNDARY 放入到AutoreleasePoolPage的next位置的地方
objc_autoreleasePoolPop
C++ void objc_autoreleasePoolPop(void *ctxt) { AutoreleasePoolPage::pop(ctxt); } void AutoreleasePoolPage::pop(void *token) { AutoreleasePoolPage *page; id *stop; return popPage<false >(token, page, stop); } template <bool allowDebug> static void popPage(void *token, AutoreleasePoolPage *page, id *stop) { page->releaseUntil(stop); } //stop 最接近的一个POOL_BOUNDARY 的地址 void releaseUntil(id *stop) { // Not recursive: we don't want to blow out the stack // if a thread accumulates a stupendous amount of garbage while (this ->next != stop) { // Restart from hotPage() every time, in case -release // autoreleased more objects AutoreleasePoolPage *page = hotPage(); id obj = *--page->next; if (obj != POOL_BOUNDARY) { objc_release(obj); } } } void objc_release(id obj) { if (obj->isTaggedPointerOrNil()) return ; return obj->release(); } inline void objc_object::release() { ASSERT(!isTaggedPointer()); rootRelease(true , RRVariant::FastOrMsgSend); } ALWAYS_INLINE bool objc_object::rootRelease() { return rootRelease(true , RRVariant::Fast); } bool objc_object::rootRelease(bool performDealloc, objc_object::RRVariant variant) { // don't check newisa.fast_rr; we already called any RR overrides uintptr_t carry; newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc-- //#define slowpath(x) (__builtin_expect(bool(x), 0)) // __builtin_expect((x),1) 表示 x 的值为真的可能性更大; //bool isDeallocating() { // return extra_rc == 0; if (slowpath(newisa.isDeallocating())) goto deallocate; return false; deallocate: if (performDealloc) { ((void (*)(objc_object *, SEL ))objc_msgSend)(this , @selector (dealloc)); } }
Autorelease 源码解析
[pObjecdt autorelease];
也就是看下这个里边到底在干什么
C++ // Replaced by ObjectAlloc - (id )autorelease { return _objc_rootAutorelease(self ); } id _objc_rootAutorelease(id obj) { ASSERT(obj); return obj->rootAutorelease(); } inline id objc_object::rootAutorelease() { if (isTaggedPointer()) return (id )this ; return rootAutorelease2(); } id objc_object::rootAutorelease2() { ASSERT(!isTaggedPointer()); return AutoreleasePoolPage::autorelease((id )this ); } static inline id AutoreleasePoolPage::autorelease(id obj) { id *dest __unused = autoreleaseFast(obj); return obj; } static inline id *AutoreleasePoolPage::autoreleaseFast(id obj) { //这里就跟PoolPush 方法调用逻辑是一样的了,就把这个指针入到page页面里面 }
经过上边分析,我们看下边代码情景会生发生什么
Case 1:
Objective-C NSObject *pObjecdt = nil ; @autoreleasepool { pObjecdt = [[Test alloc] init]; [pObjecdt autorelease]; NSLog(@"refcount = %d", CFGetRetainCount(pObjecdt)); } NSLog(@"refcount = %d", CFGetRetainCount(pObjecdt));
Case 2:
Objective-C @autoreleasepool { NSObject *pObjecdt1 = [[[Test alloc] init] retain]; @autoreleasepool { NSObject *pObjecdt = [[Test alloc] init]; [pObjecdt autorelease]; _objc_autoreleasePoolPrint(); } }
相关源码
Objective-C #ifndef Test_h #define Test_h@interface Test : NSObject { } -(void )dealloc; + (NSString *)description;@end #endif /* Test_h */
Objective-C #import <Foundation/Foundation.h> #import "Test.h"@implementation Test -(void )dealloc { NSLog(@"Test Destory!"); } + (NSString *)description { return @"Test"; }@end
总结:
也就是auto release pool 只有调用对象调用 auto release才会起作用,如果没有,可以忽略这个auto release pool