一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第27天,点击查看活动详情。
引用计数:
-当我们创建一个实例对象,它的引用计数为1;
-当我们向一个对象发送retain消息,它的引用计数+1;
-当我们向一个对象发送release消息,它的引用计数-1;
-当我们向一个对象发送autorelease消息,它的引用计数会在当前自动释放池的末尾-1;
-当一个对象的引用计数减到0,它的内存会被回收。
在MRC下如下代码:
@autoreleasepool{
NSObject *obj = [[NSObject alloc] init] autorelease];
}
通过clang命令可以转换成cpp源代码,在代码中包含__AtAutoreleasePool结构体如下:
//自动释放池结构体
struct __AtAutoreleasePool {
__AtAutoreleasePool(){/*构造函数*/
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
~__AtAutoreleasePool(){/*析构函数*/
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
void *atautoreleasepoolobj;
};
该结构体有一个void *类型成员变量atautoreleasepoolobj,保存了构造函数中调用objc_autoreleasePoolPush()的返回值,在析构函数中调用objc_autoreleasePoolPop(atautoreleasepoolobj),将该值传递了进去。
搜索源码,可以看到objc_autoreleasePoolPush()内部调用了AutoreleasePoolPage::push(),objc_autoreleasePoolPop(ctxt)内部调用了AutoreleasePoolPage::pop(ctxt)。
重点来到了AutoreleasePoolPage,AutoreleasePoolPage继承自AutoreleasePoolPageData,它的结构如下:
struct AutoreleasePoolPageData{
magic_t const magic; // 16
__unsafe_unretained id *next; // 8
pthread_t const thread; // 8
AutoreleasePoolPage * const parent; // 8
AutoreleasePoolPage *child; // 8
uint32_t const depth; // 4
uint32_t hiwat; // 4
}
class AutoreleasePoolPage : private AutoreleasePoolPageData{}
- magic:用来校验AutoreleasePoolPage的结构是否完整
- next:指向栈顶,也就是最新入栈的autorelease对象的下一个位置
- thread:指向当前线程
- parent:指向父节点
- child:指向子节点
- depth:表示链表的深度,也就是链表节点的个数
- hiwat:表示high water mark(最高水位标记)
通过数据结构,我们可以看到AutoreleasePoolPage是一个双向链表结构,parent指向了父节点,child指向了子节点。那么整个的自动释放池到底是怎么运作起来的呢?接下来我们将重点分析一下AutoreleasePoolPage::push()和AutoreleasePoolPage::pop(ctxt)这2个方法。
static inline void *push() {
id *dest = autoreleaseFast(POOL_BOUNDARY);
return dest;
}
去掉
DebugPoolAllocation调试的if分支,内部调用了autoreleaseFast(POOL_BOUNDARY),返回了dest。# define POOL_BOUNDARY nil,则表示push的时候传入了的POOL_BOUNDARY是一个nil,POOL_BOUNDARY也叫哨兵对象。
static inline id *autoreleaseFast(id obj){
AutoreleasePoolPage *page = hotPage();//获取hotPage
if (page && !page->full()) {
//page存在且没有满,则添加obj(这里obj是POOL_BOUNDARY)
return page->add(obj);
}
else if (page) {
//page存在且已满,传入obj和page(这里obj是POOL_BOUNDARY)
return autoreleaseFullPage(obj, page);
}
else {
//page不存在的情况下
return autoreleaseNoPage(obj);
}
}
这个函数做的是一个初步判定的工作:如果当前
hotPage存在且未满,则page-add(obj);如果page存在且已满则调用autoreleaseFullPage(obj, page);page不存在的情况下调用autoreleaseNoPage(obj)。按照逻辑来时首次应当是不存在hotPage的,所以我们先看看autoreleaseNoPage(obj)。
static __attribute__((noinline)) id *autoreleaseNoPage(id obj){
bool pushExtraBoundary = false;
if (haveEmptyPoolPlaceholder()) {
//是否有占位
pushExtraBoundary = true;
}
else if (obj == POOL_BOUNDARY && !DebugPoolAllocation) {
//设置一个占位
return setEmptyPoolPlaceholder();
}
//有占位后就初始化一个page。并设置为hotPage
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
//将哨兵对象加入作为边界的标记
if (pushExtraBoundary) {
page->add(POOL_BOUNDARY);
}
//将请求的对象加入
return page->add(obj);
}
打印obj对象如下:
(lldb) po obj
<__NSBundleTables: 0x1007217a0>