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 #import "Test.h" @implementation Test -(void)dealloc { NSLog(@"Test Destory!"); } + (NSString *)description { return @"Test"; } @end |
总结:
也就是auto release pool 只有调用对象调用 auto release才会起作用,如果没有,可以忽略这个auto release pool