1、AutoReleasePool
在main函数内部添加如下代码:
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[[Person alloc] init] autorelease];
}
return 0;
}
转成c++代码 后:
int main(int argc, const char * argv[]) {
{
__AtAutoreleasePool __autoreleasepool;
Person *person = [[[Person alloc] init] autorelease];
}
return 0;
}
可以发现@autoreleasepool底层是创建了一个AtAutoreleasePool类型的结构体变量,AtAutoreleasePool结构如下:
struct __AtAutoreleasePool {
__AtAutoreleasePool() {// 构造函数,在创建结构体的时候调用
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
~__AtAutoreleasePool() {// 析构函数,在结构体销毁的时候调用
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
void * atautoreleasepoolobj;
};
内部有构造函数和析构函数,分别调用了push和pop方法,所以可以将main函数里的@autoreleasepool理解成:
atautoreleasepoolobj = objc_autoreleasePoolPush();
Person *person = [[[Person alloc] init] autorelease];
objc_autoreleasePoolPop(atautoreleasepoolobj);
总结:AutoReleasePool是一个结构体,创建时调用push方法,销毁时调用pop方法。
2、AutoReleasePoolPage
通过查看 objc源码 ,NSObject.mm文件中发现,pop和push方法里面都是操作AutoreleasePoolPage的方法
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
NEVER_INLINE
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
所以需要了解一下AutoreleasePoolPage,精简过的AutoreleasePoolPage结构如下:
class AutoreleasePoolPage
{
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const deptch;
uint32_t hiwat;
}
每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址,所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起:
3、push和pop
当执行push方法时,会将先POOL_BOUNDARY存入page对象中,并返回其地址。
page其余空间会保存autorelease对象,当一个page不够时,会存入下一个page。
当执行pop方法时,将之前保存的POOL_BOUNDARY地址传入,从最后一个保存的autorelease对象开始释放,直到遇到POOL_BOUNDARY。
@autoreleasepool {// push
for (int i = 0; i < 1000; i++) {
NSObject *obj = [[[NSObject alloc] init] autorelease];
}
}// pop
执行流程伪代码如下:
atautoreleasepoolobj = objc_autoreleasePoolPush();
atautoreleasepoolobj = 0x1038; // POOL_BOUNDARY地址
for (int i = 0; i < 1000; i++) {
NSObject *obj = [[[NSObject alloc] init] autorelease];
}
objc_autoreleasePoolPop(0x1038); // POOL_BOUNDARY地址
如果是循环嵌套的就会传入多个autoreleasepool:
@autoreleasepool {// push POOL_BOUNDARY1
NSObject *obj = [[[NSObject alloc] init] autorelease];
@autoreleasepool {// push POOL_BOUNDARY2
NSObject *obj = [[[NSObject alloc] init] autorelease];
@autoreleasepool {// push POOL_BOUNDARY3
NSObject *obj = [[[NSObject alloc] init] autorelease];
}// pop POOL_BOUNDARY3
}// pop POOL_BOUNDARY2
}// pop POOL_BOUNDARY1
就会依次插入POOL_BOUNDARY并返回地址,在执行pop时,根据传入的POOL_BOUNDARY地址进行释放。
4、查看自动释放池的情况
可以通过以下私有函数来查看自动释放池的情况,先生命私有函数:
extern void _objc_autoreleasePoolPrint(void);
调用并查看打印结果:
@autoreleasepool {
NSObject *obj = [[[NSObject alloc] init] autorelease];
@autoreleasepool {
NSObject *obj = [[[NSObject alloc] init] autorelease];
@autoreleasepool {
NSObject *obj = [[[NSObject alloc] init] autorelease];
_objc_autoreleasePoolPrint();
}
}
}
打印结果:
objc[50538]: ##############
objc[50538]: AUTORELEASE POOLS for thread 0x1000ebe00
objc[50538]: 6 releases pending.
objc[50538]: [0x10680b000] ................ PAGE (hot) (cold)
objc[50538]: [0x10680b038] ################ POOL 0x10680b038
objc[50538]: [0x10680b040] 0x1007579a0 NSObject
objc[50538]: [0x10680b048] ################ POOL 0x10680b048
objc[50538]: [0x10680b050] 0x100756a00 NSObject
objc[50538]: [0x10680b058] ################ POOL 0x10680b058
objc[50538]: [0x10680b060] 0x100755e50 NSObject
objc[50538]: ##############
Program ended with exit code: 0
通过打印可以看到自动释放池里当前保存了6个内容,分别是插入的三个POOL_BOUNDARY和三个带释放的NSObject对象。
添加多个自动释放对象查看释放情况:
@autoreleasepool {
for (int i = 0; i < 1000; i++) {
NSObject *obj = [[[NSObject alloc] init] autorelease];
}
_objc_autoreleasePoolPrint();
}
打印结果:
objc[50668]: ##############
objc[50668]: AUTORELEASE POOLS for thread 0x1000ebe00
objc[50668]: 1001 releases pending.
objc[50668]: [0x100810000] ................ PAGE (full) (cold)
objc[50668]: [0x100810038] ################ POOL 0x100810038
很多autorelease对象地址,省略
objc[50668]: [0x10080c000] ................ PAGE (hot)
很多autorelease对象地址,省略
objc[50668]: ##############
Program ended with exit code: 0
由于一个page对象不够存储,所以创建一个新的page,page后面的 (hot) 代表最后一个对象保存在当前页。
5、对象的autorelease方法
NSObject *obj = [[[NSObject alloc] init] autorelease];
可以通过objc源码中的NSObject.mm文件查看实现,最终会调用:
AutoreleasePoolPage::autorelease((id)this);
将对象插入AutoreleasePoolPage中,所以对象调用autorelease方法就是将自己的地址插入AutoreleasePoolPage。
6、autorelease和Runloop
在viewDidLoad方法中添加一个autorelease观察释放情况(MRC环境下):
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[[Person alloc] init] autorelease];
NSLog(@"%s", __func__);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"%s", __func__);
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
NSLog(@"%s", __func__);
}
打印结果:
~: -[ViewController viewDidLoad]
~: -[ViewController viewWillAppear:]
~: -[Person dealloc]
~: -[ViewController viewDidAppear:]
可以看到person并不是在viewdidload方法执行结束时释放的。
autorelease对象释放是由RunLoop控制的,当进入Loop时调用push方法,休眠前调用pop方法和push方法,退出时调用pop方法。
所以person可能是在所在RunLoop循环中休眠前释放的。