objc_autorelease内部主要函数
// - __AtAutoreleasePool 结构体
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
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
};
static inline void *push() {
return autoreleaseFast(POOL_BOUNDARY);
}
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
1. return autoreleaseNoPage(obj);
}
}
//压栈操作:将对象加入AutoreleaseNoPage并移动栈顶的指针
id *add(id obj) {
id *ret = next;
*next = obj;
next++;
return ret;
}
//当前hotPage已满时调用, 新建page, 并将新建的page设置为hotpage, 然后压栈obj
static id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page) {
do {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->add(obj);
}
//当前hotpage不存在时调用, 表示第一次创建page, 就新建page并将新建的page设置为hotpage,然后压栈obj
static id *autoreleaseNoPage(id obj) {
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
if (obj != POOL_SENTINEL) {
page->add(POOL_SENTINEL);
}
return page->add(obj);
}
static inline void pop(void *token){
AutoreleasePoolPage *page;
id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
// 忽略无关代码
...
} else {
page = pageForPointer(token);
}
stop = (id *)token;
// 忽略无关代码
...
return popPage<false>(token, page, stop);
}
// - 根据 token所在的page.
static AutoreleasePoolPage *pageForPointer(const void *p) {
return pageForPointer((uintptr_t)p);
}
static AutoreleasePoolPage *pageForPointer(uintptr_t p){
AutoreleasePoolPage *result;
#define PAGE_SIZE I386_PGBYTES
#define I386_PGBYTES 4096
uintptr_t offset = p % SIZE;
ASSERT(offset >= sizeof(AutoreleasePoolPage));
result = (AutoreleasePoolPage *)(p - offset);
result->fastcheck();
return result;
}
static void popPage(void *token, AutoreleasePoolPage *page, id *stop)
{
...
page->releaseUntil(stop);
...
if (page->child) {
// hysteresis: keep one empty child if page is more than half full
if (page->lessThanHalfFull()) {
page->child->kill();
}else if (page->child->child) {
page->child->child->kill();
}
}
}
// - 给page中的对象发送release消息.
void releaseUntil(id *stop) {
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
while (this->next != stop) {
// Restart from hotPage() every time, in case -release
// autoreleased more objects
AutoreleasePoolPage *page = hotPage();
// fixme I think this `while` can be `if`, but I can't prove it
while (page->empty()) {
page = page->parent;
setHotPage(page);
}
page->unprotect();
// page->next代表下一个需要插入的位置,此时是没有值的,但是上一个8字节地址是有值的,根据取值符*可以拿到对应的obj指针
id obj = *--page->next;
// 将page中next指向的位置改为SCRIBBLE
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
if (obj != POOL_BOUNDARY) {
objc_release(obj);
}
}
setHotPage(this);
...
}
void kill() {
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
AutoreleasePoolPage *page = this;
while (page->child) page = page->child;
AutoreleasePoolPage *deathptr;
do {
deathptr = page;
page = page->parent;
if (page) {
page->unprotect();
page->child = nil;
page->protect();
}
delete deathptr;
} while (deathptr != this);
}
// - objc_autoreleaseReturnValue -> objc_autorelease()->objc_object::autorelease()->rootAutorelease()->objc_object::rootAutorelease2()->AutoreleasePoolPage::autorelease()
id objc_autoreleaseReturnValue(id obj){
if (prepareOptimizedReturn(ReturnAtPlus1)) return obj;
return objc_autorelease(obj);
}
// - 这里 autorelease 又调用到 autoreleaseFast.
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;
}
结构
![](https://img-blog.csdnimg.cn/img_convert/de8f1a459cc2fa4879832439c97b241c.webp?x-oss-process=image/format,png)
image.png
AutoreleasePoolPage 结构
- 首先AutoreleasePool 是一个由多个AutoreleasePoolPage组成的双向链表, 每个AutoreleasePoolPage 都有parent和child指针, 同时每个AutoreleasePoolPage又都是一个栈, 每个AutoreleasePoolPage的大小都是 4096byte(4k). 其中
- next id*类型,指向当前page中的下一个可插入位置.
- thread 当前page所在的线程
- parent 前一个page
- child 后一个page
- depth 深度,代表当前page在整个双向链表中的位置
objc_autoreleasePoolPush(void)
- 调用流程: objc_autoreleasePoolPush(void) -> AutoreleasePoolPage::push() -> autoreleaseFast(POOL_BOUNDARY);
- 每次push时候都会插入一个POOL_BOUNDARY(哨兵对象).
- hotpage存在并且没有满
- 保存obj变量, 然后直接移动next指针.
- hotpage满了
- 顺着page->child沿着链表查找下一个不满的page并将这个查找到的page设置为hotpage, 然后将变量保存到hotpage中.
- 如果查找的最后一个hotpage都是满的, 直接新建一个page,并将这个page设置为hotpage, 然后将变量保存到hotpage中.
- hotpage不存在
- 新建一个page,并将这个page设置为hotpage, 然后将变量保存到hotpage中.
- hotpage存在并且没有满
objc_autoreleasePoolPop
- 调用流程: objc_autoreleasePoolPop(atautoreleasepoolobj) -> AutoreleasePoolPage::pop() -> pageForPointer();
- 函数内部实现原理:
- 找到hotpage,即pageN.
- 校验pageN是否为空,否则循环寻找pageN的父结点并赋值为hotpage
- 从hotpage的next减8字节,即page中最后一个autorelease对象开始,依次执行release操作,同时将page中的当前位置赋值为SCRIBBLE
4.循环执行1-3直到当前page的next指向token(如果token不为POOL_BOUNDARY, 也执行release). - 当前page设置为hotpage.
autoreleasepool 生命周期
-
系统创建(有些函数、方法需要返回一个对象,而系统可能在该对象被返回之前,就已经销毁了对象。那么为了保证函数、方法返回的对象在被返回之前不被销毁,我们就要使用自动释放池进行延迟销毁,如 .
[NSString string]和[[NSString alloc]init]
第一个因为是string函数的返回值无法在作用域结束后直接释放, 会自动加入到runloop创建的自动释放池中, 而使用alloc]init] 创建的对象, 超过作用域就直接释放, 不用加入到自动释放池中.)- 即将进入Loop, 回调内部调用 _objc_autoreleasePoolPush() 创建自动释放池。
- BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池.
-
自己使用@autoreleasepool {}
- 会在}后, 调用 objc_autoreleasePoolPop(), 释放内部的变量.
哨兵对象
1.#define POOL_BOUNDARY nil
- 每当自动释放池初始化调用objc_autoreleasePoolPush方法时,总会通过AutoreleasePoolPage的push方法,将POOL_BOUNDARY放到当前page的栈顶,并且返回这个边界对象;而在自动释放池释放调用objc_autoreleasePoolPop方法时,又会将边界对象以参数传入,这样自动释放池就会向释放池中对象发送release消息,直至找到第一个边界对象为止。
返回值自动释放的原理
![](https://img-blog.csdnimg.cn/img_convert/1115c800f4be752cd90901ffa4f841a6.webp?x-oss-process=image/format,png)
image.png
![](https://img-blog.csdnimg.cn/img_convert/0e8233d7c23d191dc661f8d557886067.webp?x-oss-process=image/format,png)
image.png
图中可以看到[DemoObject object]方法最终调用了objc_autoreleaseReturnValue方法, 而objc_autoreleaseReturnValue中调用到了autoreleaseFast.
测试
// DemoObject
@interface DemoObject : NSObject
@end
@implementation DemoObject
+ (DemoObject*)object {
DemoObject *object = [[DemoObject alloc]init];
return object;
}
@end
// AutoreleasePoolDemoVC
@interface AutoreleasePoolDemoVC ()
@end
@implementation AutoreleasePoolDemoVC
- (void)viewDidLoad {
[super viewDidLoad];
__weak id object1;
{
object1 = [DemoObject object];
}
NSLog(@"object1 = %@", object1);
__weak id object2;
{
object2 = [[DemoObject alloc]init];
}
NSLog(@"object2 = %@", object2);
}
// - 第一个log打印的结果不是空的, 第二个打印的结果是空的
// - 因为第一个使用[DemoObject object] 生成的对象会被加入到runloop创建的自动释放池中, 而runloop创建的自动释放池, 需要在runloop休眠或者退出时候才会销毁.
// - 第二个是使用alloc init出来的对象, 对象在超过{}的作用域时候, 就会释放.