自动释放是在什么时候释放的
在MRC环境下,释放对象有两种方式一种是创建完对象调用autorelease,或者是手动release。为了方便使用对象,一般情况下都是调用autorelease。
要想查看到调用了autorelease的对象什么时候会被释放,就要先研究一下,autoreleasepool,表面上看是在autoreleasepool的大括号结束的时候,就释放了对象。将代码转成CPP文件查看autoreleasepool的结构,
@autoreleasepool
{
Person *person = [[Person alloc] init];//正常情况出了大括号就会自动销毁
}
//转成cpp
{ __AtAutoreleasePool __autoreleasepool;
//相当于在这里调用了构造函数
atautoreleasepoolobj = objc_autoreleasePoolPush();
Person *person = [[Person alloc] init];
//在大括号结束的时候调用了析构函数
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
struct __AtAutoreleasePool {
__AtAutoreleasePool() {//构造函数,在结构体创建的时候调用
atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {//析构函数,在结构体销毁的时候调用
objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
以此类推,没增加一个autoreleasepool就会多增加一次上面的结构
02 objc_autoreleasePoolPush和objc_autoreleasePoolPop做的事情
要清楚autorelease对象什么时候销毁,就要高清这两个函数做了什么事情
通过源码可以看出他们通过方法调用来操作autoreleasepoolPage
自动释放池的主要底层数据结构是:__AtAutoreleasePool、AutoreleasePoolPage
调用了autorelease的对象,他的生命周期最终都是通过AutoreleasePoolPage对象来管理的
源码分析
clang重写@autoreleasepool
objc4源码:NSObject.mm
autoreleasepoolPage
每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址
所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起
双向链表就是两个对象产生联系,左边的对象有个指针指向右边的对象,右边的对象也有个指针指向左边的对象,
因为内存是有限的,一个autoreleasepoolpage只有4096当不够的时候就会使用新的page
AutoreleasePoolPage里面一个begin和end一点调用begin他就会返回存放autorelease对象的首个地址,其实就是用this指针+sizeof(this)
page的双向链表
03push和pop做的事情
调用push方法会将一个POOL_BOUNDARY入栈,并且返回其存放的内存地址,然后将autorelease对象放在这个地址后面,如果这个autoreleasepoolPage空间放满了,那么会创建一个新的page来存放autorelease对象,
调用pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY
id *next指向了下一个能存放autorelease对象地址的区域,
当出现多个autoreleasepool嵌套的时候,没出现一个push都会加入一个POOL_BOUNDARY
04
poolPage的结构,
如果是有多个autoreleasepool对象,如果page的内存够,他们会放到同一个page中,这一个page满了之后才会往下面存
objc[1228]: ##############
objc[1228]: AUTORELEASE POOLS for thread 0x1000d2dc0
objc[1228]: 7 releases pending.
objc[1228]: [0x101803000] ................ PAGE (hot) (cold)
objc[1228]: [0x101803038] ################ POOL 0x101803038
objc[1228]: [0x101803040] 0x10051b200 Person
objc[1228]: [0x101803048] 0x100519700 Person
objc[1228]: [0x101803050] ################ POOL 0x101803050
objc[1228]: [0x101803058] 0x100519290 Person
objc[1228]: [0x101803060] 0x1005188c0 Person
objc[1228]: [0x101803068] 0x100516cf0 NSObject
objc[1228]: ##############
cold和hot
hot是指当前正在使用的page,如果我们取对象是从当前page中开始,那么当前的page就可以称作hot
05push和pop的具体调用过程
autorelease和runloop01
autorelease对象会在什么时机调用release
如果这个对象是被一个autoreleasepool包裹住的话,他的release时机就在这个大括号的结束
如果没有的话,
autorelease和runloop02
runloop其实在主线程中注册了两个observer的
第1个Observer监听了kCFRunLoopEntry事件,会调用objc_autoreleasePoolPush()
第2个Observer
监听了kCFRunLoopBeforeWaiting事件,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
监听了kCFRunLoopBeforeExit事件,会调用objc_autoreleasePoolPop()
这个对象什么时候释放是有runloop控制的
他可能是在某次循环中,runloop休眠之前调用了release
方法中有局部对象,出了括号就马上释放么?
如果这个对象是通过autorelease的方式的话,不是马上释放,要等那次runloop休眠之前释放,如果是普通的arc代码是会马上释放的
网友评论