- 自动释放池的主要底层数据结构是:
__AtAutoreleasePool、AutoreleasePoolPage - 调用了
autorelease的对象最终都是通过AutoreleasePoolPage对象来管理的 是和线程一一对应的
@autoreleasepool {//...}// 编译器会将上面代码改写为一下代码void *ctx = objc_autoreleasePoolPush();//{}中的代码objc_autoreleasePoolPop(ctx)
源码分析
clang重写@autoreleasepool
- objc4源码:NSObject.mm
AutoreleasePoolPage的结构
- 每个
AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址 - 所有的
AutoreleasePoolPage对象通过以栈为结点,双向链表的形式连接在一起

- 调用
push方法会将一个POOL_BOUNDARY入栈,并且返回其存放的内存地址 - 调用
pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY id *next指向了下一个能存放autorelease对象地址的区域
Runloop和Autorelease
App启动后,iOS在主线程的Runloop中注册了2个Observer管理和维护AutoreleasePool,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler(),打印currentRunLoop可以看到。
<CFRunLoopObserver 0x6080001246a0 [0x101f81df0]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x1020e07ce), context = <CFArray 0x60800004cae0 [0x101f81df0]>{type = mutable-small, count = 0, values = ()}}<CFRunLoopObserver 0x608000124420 [0x101f81df0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x1020e07ce), context = <CFArray 0x60800004cae0 [0x101f81df0]>{type = mutable-small, count = 0, values = ()}}
- 第1个
Observer监听了kCFRunLoopEntry事件,会调用objc_autoreleasePoolPush()创建自动释放池,其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。 - 第二个
Observer监视了两个事件,这个 Observer 的 order 是2147483647,优先级最低,保证其释放池发生在其他所有回调之后- 监听了
kCFRunLoopBeforeWaiting(准备进入休眠)时,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()释放旧的池并创建新池; - 监听了
kCFRunLoopBeforeExit事件,会调用objc_autoreleasePoolPop()
- 监听了

主线程的其他操作通常均在这个AutoreleasePool之内(main函数中),以尽可能减少内存维护操作(当然你如果需要显式释放【例如循环】时可以自己创建AutoreleasePool否则一般不需要自己创建)。
常见面试题:
1. 自动释放池是什么时候创建的?什么时候销毁的?
- 创建,运行循环检测到事件并启动后,就会创建自动释放池
- 销毁:一次完整的运行循环结束之前,会被销毁
2. 以上代码是否有问题?如果有,如何解决?
for (long i = 0; i < largeNumber; ++i) {NSString *str = [NSString stringWithFormat:@"hello - %ld", i];str = [str uppercaseString];str = [str stringByAppendingString:@" - world"];}
解决方法:引入自动释放池
- 1> 外面加自动释放池(快?):能够保证for循环结束后,内部产生的自动释放对象,都会被销毁,需要等到 for 结束后,才会释放内存
- 2> 里面加自动释放池(慢?):能够每一次 for 都释放产生的自动释放对象!
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {NSLog(@"start");CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();[self answer1];NSLog(@"外 %f", CFAbsoluteTimeGetCurrent() - start);start = CFAbsoluteTimeGetCurrent();[self answer2];NSLog(@"内 %f", CFAbsoluteTimeGetCurrent() - start);}- (void)answer1 {@autoreleasepool {for (long i = 0; i < largeNumber; ++i) {NSString *str = [NSString stringWithFormat:@"hello - %ld", i];str = [str uppercaseString];str = [str stringByAppendingString:@" - world"];}}}- (void)answer2 {for (long i = 0; i < largeNumber; ++i) {@autoreleasepool {NSString *str = [NSString stringWithFormat:@"hello - %ld", i];str = [str uppercaseString];str = [str stringByAppendingString:@" - world"];}}}
- 实际测试结果,是运行循环放在内部的速度更快!
- 日常开发中,如果遇到局部代码内存峰值很高,可以引入自动释放池及时释放延迟释放对象
