一、autoreleasepool源码解释
AutoreleasePool是一个堆栈,里面装着指针。那么栈的底层实现是什么呢?是数组。
AutoreleasePool全名叫NSAutoreleasePool。它就是一个对象引用计数自动处理器,在官方文档中被称为是一个类。
在ARC中,在遵守一些规则的情况下,可以自动释放对象。系统自动帮对象调用了autorelease方法,然后就会把对象扔进池里面,等一次runloop结束,这个池会被系统销毁,池里面的对象也就跟着被销毁了。
NSAutoreleasePool可以同时有多个,它的组织是个栈,总是存在一个栈顶pool,也就是当前pool,每创建一个pool,就往栈里压一个,改变当前pool为新建的pool,然后,每次给pool发送drain消息,就弹出栈顶的pool,改当前pool为栈里的下一个 pool。
跟MRC的release方法比较就是延迟了对象的销毁的时间。但autoreleasepool依然不是.Net/Java那种全自动的垃圾回收机制。
二、@autoreleasepool{} C++文件
@autoreleasepool
到底是什么?我们在命令行中使用 clang -rewrite-objc main.m
让编译器重新改写这个文件。
如果报错'UIKit/UIKit.h' file not found ,解决方法如下:
1.进入终端,键入命令 vim ~/.bash_profile
2.在vim界面输入i进入编辑编辑状态并且键入:alias rewriteoc='clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'
3.键入完毕,点esc退出编辑状态,再键入:wq退出vim并保存,执行source ~/.bash_profile<-这句一定要执行,执行才会生效
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk xxxxx.m
编译到的main.cpp文件主要代码如下:
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool};@autoreleasepool是一个_AtAutoreleasePool的结构体。_AtAutoreleasePool的结构体里的方法如下:
struct __AtAutoreleasePool { __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();} ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);} void * atautoreleasepoolobj; };
autoreleasepool里是调用 栈结构的push() 和 pop() 操作。
三、autoreleasepool结构
综上两点得出autoreleasepool结构如下图:
autoreleasepool底层是由autoreleasepoolpage组成的双向链表结构。
四、autoreleasepoolpage组成
magic_t const magic; //用来校验 AutoreleasePoolPage 的结构是否完整 id *next;//指向最新添加的 autoreleased 对象的下一个位置,初始化时指向 begin() pthread_t const thread;//指向当前线程 AutoreleasePoolPage * const parent;// 指向父结点,第一个结点的 parent 值为 nil AutoreleasePoolPage *child;//指向父结点,第一个结点的 parent 值为 nil uint32_t const depth;//链表的深度,节点个数 uint32_t hiwat;//high water mark 数据容纳的一个上限 PAGE_MAX_SIZE;//size大小为4096,虚拟内存每个扇区4096个字节,4K对齐的说法 COUNT;//一个page里的对象数 POOL_BOUNDARY;//边界对象,以前为POOL_SENTINEL哨兵对象
五、autoreleasepool 运行过程
主要过程:
AutoreleasePoolPage::push()
AutoreleasePoolPage::autorelease((id)this);
AutoreleasePoolPage::pop(ctxt)
源码代码示例:
——————— push —————— void * objc_autoreleasePoolPush(void) { return AutoreleasePoolPage::push(); } static inline void *push() { id *dest; if (DebugPoolAllocation) { // Each autorelease pool starts on a new pool page. dest = autoreleaseNewPage(POOL_BOUNDARY); } else { dest = autoreleaseFast(POOL_BOUNDARY); } assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY); return dest; } static inline id *autoreleaseFast(id obj) { AutoreleasePoolPage *page = hotPage();//coldpage() if (page && !page->full()) { return page->add(obj); } else if (page) { return autoreleaseFullPage(obj, page); } else { return autoreleaseNoPage(obj);//完全没有page,重新创建一个page,并把对象加入page中 } } id *add(id obj) { id *ret = next; // faster than `return next-1` because of aliasing *next ++ = obj; return ret; } —————— autorelease —————— // Replaced by ObjectAlloc - (id)autorelease { return ((id)self)->rootAutorelease(); } __attribute__((noinline,used)) id objc_object::rootAutorelease2() { assert(!isTaggedPointer()); return AutoreleasePoolPage::autorelease((id)this); } static inline id autorelease(id obj) { id *dest __unused = autoreleaseFast(obj); assert(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj); return obj; } autoreleaseFast() ——————— pop —————— void objc_autoreleasePoolPop(void *ctxt) { AutoreleasePoolPage::pop(ctxt); } static inline void pop(void *token) { AutoreleasePoolPage *page; id *stop; page = pageForPointer(token); stop = (id *)token; page->releaseUntil(stop); } void releaseUntil(id *stop) { while (this->next != stop) { AutoreleasePoolPage *page = hotPage(); id obj = *--page->next; if (obj != POOL_BOUNDARY) { objc_release(obj); } } } __attribute__((aligned(16))) void objc_release(id obj) { if (!obj) return; if (obj->isTaggedPointer()) return; return obj->release(); } inline void objc_object::release() { assert(!isTaggedPointer()); if (fastpath(!ISA()->hasCustomRR())) { rootRelease(); return; } ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_release); }
六、验证
1.下载源码
2.查看验证page的内容。在源码的main.m里输入一个字符串,打上断点:
int main(int argc, const char * argv[]) { @autoreleasepool { NSString * s1 = [NSString stringWithFormat:@"8pmedu28pmedu28pmedu--S1"]; } return 0; }
3.在源码main.m里使用以下命令,查看当前page页内容
expr AutoreleasePoolPage::hotPage()
(lldb) expr AutoreleasePoolPage::hotPage() ((anonymous namespace)::AutoreleasePoolPage *) $1 = 0x0000000102803000
p *$1
p *$1 ((anonymous namespace)::AutoreleasePoolPage) $2 = { magic = { m = ([0] = 2711724449, [1] = 1330926913, [2] = 1162626386, [3] = 558191425) } next = 0x0000000102803048 thread = 0x00000001009ba380 parent = 0x0000000000000000 child = 0x0000000000000000 depth = 0 hiwat = 0 }
p $1.printAll()
(lldb) p $1.printAll() objc[1756]: ############## objc[1756]: AUTORELEASE POOLS for thread 0x1009ba380 objc[1756]: 2 releases pending. objc[1756]: [0x102803000] ................ PAGE (hot) (cold) objc[1756]: [0x102803038] ################ POOL 0x102803038 objc[1756]: [0x102803040] 0x100c37fc0 __NSCFString objc[1756]: ############## Fix-it applied, fixed expression was: $1->printAll()
验证 POOL 0x102803038的内存地址a.打印出next[-1] pool_boundary所在的值
p $0.next[-2]
(id) $11 = nil Fix-it applied, fixed expression was: $0->next[-2]b.取地址符
p &$11
(id *) $12 = 0x000000010100a038
c.比较b的结果和p $1.printAll()里POOL 的地址值。