autoreleasePool
App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。可以通过LLDB的po [NSRunLoop currentRunLoop]
进行查看
第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。
在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool 了。
当我们使用@autoreleasepool{}时,编译器会将其转换成以下形式
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
}
//__AtAutoreleasePool定义如下:
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
.
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
// `objc_autoreleasePoolPush()`最终会调用`autoreleaseFast()`压入`POOL_SENTINEL(哨兵对象)`,并返回哨兵地址方便`objc_autoreleasePoolPop`
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
// 向晚于哨兵地址之后的对象发送`release`
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
//`autorelease`最终也是调用`autoreleaseFast()`,为了将对象加入`pool`中
static inline id autorelease(id obj)
{
ASSERT(obj);
ASSERT(!obj->isTaggedPointer());
id *dest __unused = autoreleaseFast(obj);
ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}
对象何时释放
- 手动添加的
@autoreleasepool{}
,会在}
结束的时候内部会调用objc_autoreleasePoolPop
进行释放 - 添加到系统
autoreleasepool
的对象,会在runloop进入睡眠或者退出runloop的时候释放
内存
无论是打开网页,还是执行一段简单的 js 代码,UIWebView 都会占用大量内存,同时旧版本的 css 动画也会导致大量问题,所以最好使用 WKWebView。
内存泄漏