iOS

Autorelease

Posted by summerxx on February 22, 2017

前言: 自动变量,在计算机编程中, 是一个局部变量,当程序流进入并离开变量的范围时,该变量自动分配和释放.点击查看详细解释 , 由此引出这篇文章的主角 autorelease.

autorelease

解释: 类似于C语言中的自动变量, 超出其作用域(有效范围)便自动废弃

1
2
3
4
{
int a;
}
// 变量 a 被废弃

不同于 C语言自动变量的是 在Objective-C中可以设定变量的作用域, 具体方法如下:

  • 生成并持有NSAutoreleasePool
  • 调用autorelease方法
  • 废弃 NSAutoreleasePool 对象
1
2
3
4
5
6
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id object = [[NSObject alloc] init];
[object autorelease];
NSLog(@"object ===== %@", object); // 正常
[pool drain];
[object class]; // crash

而实际在MRC下开发, 没有特殊要求可以不使用NSAutoreleasePool 因为在Cocoa框架中, 程序主循环NSRunLoop或者程序可运行的地方, 对这个对象(NSAutoreleasePool)的生成, 持有, 废弃, 已经进行了处理.

值得注意的是有时候不得不用, 比如 程序主循环未退出, 而生成了大量的autorelease对象, 那就会造成内存不足的现象. 所以适当使用

实际开发中不”明显”的autorelease 同样值得注意.

1
NSMutableArray *array = [NSMutableArray arrayWithCapacity:1]; // 实际上这个方法也返回了一个autorelease对象.

伪代码arrayWithCapacity

1
2
3
4
5
- (id)arrayWithCapacity:(NSUInteger)num {
NSMutableArray *obj = [[NSMutableArray alloc] init];
[obj autorelease]; // 这里使用了autorelease
return obj;
}

autorelease如何实现

autoreleasepool 是没有单独的内存结构的,它是通过以 AutoreleasePoolPage 为结点的双向链表来实现的,在 runtime/NSObject.mm 581行可以找到相应的实现.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
id *add(id obj)
{
// 将对象加到内部数组中
}

void releaseAll() 
{
// 调用内部数组中的对象的release实例方法 
}
static inline id autorelease(id obj)
{
// 取得正在使用的 AutoreleasePoolPage 实例
}
static inline void *push() 
{
// 相当于生成或持有 NSAutoreleasePool类对象
}

static inline void pop(void *token) 
{
// 相当于废弃NSAutoreleasePool 类对象
}

引用:

  • 每一个线程的 autoreleasepool 其实就是一个指针的堆栈;
  • 每一个指针代表一个需要 release 的对象或者 POOL_SENTINEL(哨兵对象,代表一个 autoreleasepool 的边界);
  • 一个 pool token 就是这个 pool 所对应的 POOL_SENTINEL 的内存地址。当这个 pool 被 pop 的时候,所有内存地址在 pool token 之后的对象都会被 release ;
  • 这个堆栈被划分成了一个以 page 为结点的双向链表。pages 会在必要的时候动态地增加或删除;
  • Thread-local storage(线程局部存储)指向 hot page ,即最新添加的 autoreleased 对象所在的那个 page 。

文章参照

NSObject.mm Objective-C Autorelease Pool 的实现原理

一起学习 共同进步 走过一段有意义的时光 我是夏天 这是我维护的群 498143780 你可以来找我交流.

我是夏天, 暖暖的夏天 End