关于@ autoreleasepool{}
在main方法中有一个@autoreleasepool{}
int main(int argc, char * argv[]) { NSString * appDelegateClassName; @autoreleasepool { // Setup code that might create autoreleased objects goes here. appDelegateClassName = NSStringFromClass([AppDelegate class]); } return UIApplicationMain(argc, argv, nil, appDelegateClassName);}@autoreleasePool{} 的底层是一个__AtAutoreleasepool结构体,这个结构体在初始化时调用objc_autoreleasePoolPush(), 析构方法时调用objc_autoreleasePoolPop()方法。
struct __AtAutoreleasePool { __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();} ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);} void * atautoreleasepoolobj;};Autoreleasepool原理
void *objc_autoreleasePoolPush(void) { return AutoreleasePoolPage::push();}void objc_autoreleasePoolPop(void *ctxt) { AutoreleasePoolPage::pop(ctxt);}由以上代码可看出,调用push和pop方法实在调用的是AutoreleasePoolPage的push和pop方法
AutoreleasePoolPage
AutoreleasePoolPage的数据结构如下:
class AutoreleasePoolPage { magic_t const magic; id *next; pthread_t const thread; AutoreleasePoolPage * const parent; AutoreleasePoolPage *child; uint32_t const depth; uint32_t hiwat;};
- magic 判断autoreleasePoolPage的完备性,获取当前page时用
- next 指向下一个对象
- thread 当前page地点的线程
- parent/child 双向链表的指针
自动开释池中AutoreleasePoolPage是用双向链表毗连起来的,parent/child 是构造双向链表的指针
AutoreleasePoolPage的栈结构如图:
栈中包罗begin()/end()方法,获取存取对象的开始、竣事地点。
next指向的是下一个对象
栈顶有一个POOL_SENTINEL哨兵对象,值为nil
#define POOL_SENTINEL nilPush()
void *objc_autoreleasePoolPush(void) { return AutoreleasePoolPage::push();}AutoreleasePoolPage内的push方法
static inline void *push() { return autoreleaseFast(POOL_SENTINEL);}主要调用的方法是autoreleaseFast
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); }}hotPage()指的是当前pape,由代码所示:
- 如果page不满时,直接在page中添加对象;
- 如果page满了,会调用autoreleaseFullPage方法,遍历双向链表,找到一个未满的page,设置为hotpage,然后在page中添加对象。
- 如果page不存在,调用autoreleaseNoPage方法,新建一个AutoreleasePoolPage对象,设置为hot Page,然后在栈顶添加哨兵对象。
add方法,实在就是压栈操作
id *add(id obj) { id *ret = next; *next = obj; next++; return ret;}Pop()
void objc_autoreleasePoolPop(void *ctxt) { AutoreleasePoolPage::pop(ctxt);}这个方法一样平常传参是哨兵对象,但是不传哨兵对象也可以。
static inline void pop(void *token) { AutoreleasePoolPage *page = pageForPointer(token); id *stop = (id *)token; page->releaseUntil(stop); if (page->child) { if (page->lessThanHalfFull()) { page->child->kill(); } else if (page->child->child) { page->child->child->kill(); } }}
- 先调用pageForPointer方法查找当前page,pageForPointer是将指针与页面巨细(4096)取模,得到当前指针的偏移量
- releaseUntil方法,release对象
- 调用child的kill方法删除当前页和子页
autorelease()
inline id objc_object::rootAutorelease() { if (isTaggedPointer()) return (id)this; if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this; return rootAutorelease2();}__attribute__((noinline,used)) id objc_object::rootAutorelease2() { return AutoreleasePoolPage::autorelease((id)this);}static inline id autorelease(id obj) { id *dest __unused = autoreleaseFast(obj); return obj;}autorelease方法实在调用的autoreleaseFast 方法,添加对象
Autorelease对象什么时间开释?
runloop中每次迭代会自动参加自动开释池的push和pop,以是Autorelease对象是在runloop迭代竣事后开释的。
runloop和autoreleasepool的关系
App启动后,苹果在主线程RunLoop里注册了两个Observe,其回调都是_wrapRunLoopWithAutoReleasePoolHandler()。
第一个Observer监听的时间是Entry(即将进入Loop),其回调内会调用_objc_autoreleasePoolPush()创建自动开释池。优先级最高,包管创建开释池发生在其他全部回调之前。
第二个Observer监听了两个变乱:BeforeWaiting(预备进入休眠)调用_objc_autoreleasePoolPop()和_objc_autoreleasePoolPush()开释旧池并创建新池;Exit(即将退出Loop)是调用_objc_autoreleasePoolPop()来开释自动开释池。优先级最低,包管其开释池发生在其他全部回到之后。
主线程实验的代码,通常是写在诸如变乱回调、Timer回调内的。这些回调被RunLoop创建好的AutoreleasePool围绕着,以是不会出现内存走漏,开发者也不必表现创建Pool。
参考链接:
自动开释池的宿世今生
内幕背后的autorelease |