一、自动释放池
在MRC环境下对象的内存管理是程序员自己管理的,当对象需要释放的时候,调用一下release,但是这样这样带来了很大的不便,代码必须要写在对象调用release的前面,这样很是不方便。为了解决这个问题,可以在创建出对象的时候自动调用一下autorelease,这样就不需要我们自己调用对象release方法了
1、autorelease
- RevanPerson
#import
@interface RevanPerson : NSObject
@end
#import "RevanPerson.h"
@implementation RevanPerson
- (void)dealloc {
NSLog(@"%s", __func__);
[super dealloc];
}
@end
- 测试代码
#import
#import "RevanPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
RevanPerson *person = [[[RevanPerson alloc] init] autorelease];
}
return 0;
}
- 打印输出
2018-07-28 23:35:42.745168+0800 09-自动释放池[61827:3974115] -[RevanPerson dealloc]
Program ended with exit code: 0
- 使用autorelease可以解决手动调用release
2、查看源码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc [OC文件] -o [.cpp文件]
- main方法
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
RevanPerson *person = ((RevanPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((RevanPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((RevanPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("RevanPerson"), sel_registerName("alloc")), sel_registerName("init")), sel_registerName("autorelease"));
}
return 0;
}
- 简化后的main方法
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ {
__AtAutoreleasePool __autoreleasepool;
RevanPerson *person = [[[RevanPerson alloc] init] autorelease];
}
return 0;
}
3、__AtAutoreleasePool结构体
struct __AtAutoreleasePool {
//构造函数,在创建结构体的时候调用
__AtAutoreleasePool() {
//构造函数会调用objc_autoreleasePoolPush函数,并且会返回 atautoreleasepoolobj指针
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
//析构函数,在结构体销毁的时候调用
~__AtAutoreleasePool() {
//析构函数,会调用objc_autoreleasePoolPop函数并且会把 atautoreleasepoolobj指针传入
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
void * atautoreleasepoolobj;
}
在执行@autoreleasepool自动释放池时,首先会生成一个__AtAutoreleasePool类型的__autoreleasepool变量,在创建这个结构体的时候,会调用objc_autoreleasePoolPush函数,并且会返回一个地址,当这个结构体将要销毁时又会调用objc_autoreleasePoolPop函数
二、AutoreleasePoolPage类结构
每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址。所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起
三、objc_autoreleasePoolPush函数
- 1、objc_autoreleasePoolPush函数
void *objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
- 2、AutoreleasePoolPage类简单结构
class AutoreleasePoolPage {
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
.
.
.
}
- 3、push函数
static inline void *push() {
id *dest;
if (DebugPoolAllocation) {
// Each autorelease pool starts on a new pool page.
// 传入POOL_BOUNDARY来新建一个AutoreleasePoolPage对象
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
// 已经有了AutoreleasePoolPage对象就直接添加
dest = autoreleaseFast(POOL_BOUNDARY);
}
assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
- 4、autoreleaseNewPage函数
static __attribute__((noinline))
id *autoreleaseNewPage(id obj) {
AutoreleasePoolPage *page = hotPage();
if (page) return autoreleaseFullPage(obj, page);
else return autoreleaseNoPage(obj);
}
- 5、autoreleaseFullPage函数,会把POOL_BOUNDARY加入到page中
static __attribute__((noinline))
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page) {
// The hot page is full.
// Step to the next non-full page, adding a new page if necessary.
// Then add the object to that page.
assert(page == hotPage());
assert(page->full() || DebugPoolAllocation);
do {
//page如果child有值,那么就取page中child中存储的那个page
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);//新创建一个page
} while (page->full());//判断这个page是否已经满了
setHotPage(page);//page空间没有满,设置为hot标志
return page->add(obj);//把obj加入到page中
}
- 执行objc_autoreleasePoolPush函数后
四、autorelease
对象调用release后会直接被释放,那么调用autorelease会怎么
- 1、autorelease方法
- (id)autorelease {
return ((id)self)->rootAutorelease();
}
- 2、rootAutorelease函数
inline id
objc_object::rootAutorelease()
{
if (isTaggedPointer()) return (id)this;
if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
return rootAutorelease2();
}
- 3、rootAutorelease2函数
__attribute__((noinline,used))
id
objc_object::rootAutorelease2()
{
assert(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
- 4、AutoreleasePoolPage的autorelease函数并且把对象传入
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;
}
- 5、autoreleaseFast函数
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {//page空间没有满
return page->add(obj);//把obj对象加入到page空间中
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
- 对象调用了autorelease方法后都会被加入到自动释放池创建的page对象中
五、objc_autoreleasePoolPop函数
当__AtAutoreleasePool结构销毁时,会调用结构体的析构函数objc_autoreleasePoolPop函数
- 1、objc_autoreleasePoolPop函数
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
- 2、调用AutoreleasePoolPage类中的pop函数,ctxt参数就是构造函数返回的地址
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
// Popping the top-level placeholder pool.
if (hotPage()) {
// Pool was used. Pop its contents normally.
// Pool pages remain allocated for re-use as usual.
pop(coldPage()->begin());
} else {
// Pool was never used. Clear the placeholder.
setHotPage(nil);
}
return;
}
page = pageForPointer(token);
stop = (id *)token;//token中存储的内容就是 POOL_BOUNDARY
if (*stop != POOL_BOUNDARY) {
if (stop == page->begin() && !page->parent) {
// Start of coldest page may correctly not be POOL_BOUNDARY:
// 1. top-level pool is popped, leaving the cold page in place
// 2. an object is autoreleased with no pool
} else {
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
return badPop(token);
}
}
if (PrintPoolHiwat) printHiwat();
//开始释放对象直到stop
page->releaseUntil(stop);
//下面是处理有多个 page 的情况
// memory: delete empty children
if (DebugPoolAllocation && page->empty()) {//如果当前的page为空
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;//找到上一个page
page->kill();
setHotPage(parent);
} else if (DebugMissingPools && page->empty() && !page->parent) {
// special case: delete everything for pop(top)
// when debugging missing autorelease pools
page->kill();
setHotPage(nil);
}
else 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();
}
}
}
- 3、releaseUntil函数
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();
id obj = *--page->next;
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
if (obj != POOL_BOUNDARY) {
objc_release(obj);
}
}
setHotPage(this);
#if DEBUG
// we expect any children to be completely empty
for (AutoreleasePoolPage *page = child; page; page = page->child) {
assert(page->empty());
}
#endif
}
- 4、对象移除objc_release
__attribute__((aligned(16)))
void
objc_release(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
return obj->release();
}
- 5、调用对象的release函数
inline void
objc_object::release()
{
assert(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
rootRelease();
return;
}
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_release);
}
- 6、给这个对象发送SEL_release消息
- 7、page销毁page->kill()
六、实践
可以通过以下私有函数来查看自动释放池的情况
extern void _objc_autoreleasePoolPrint(void);
-1、测试代码
#import
#import "RevanPerson.h"
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
RevanPerson *person1 = [[[RevanPerson alloc] init] autorelease];
RevanPerson *person2 = [[[RevanPerson alloc] init] autorelease];
RevanPerson *person3 = [[[RevanPerson alloc] init] autorelease];
_objc_autoreleasePoolPrint();
@autoreleasepool {
for (int i = 0; i < 600; i++) {
RevanPerson *person4 = [[[RevanPerson alloc] init] autorelease];
}
@autoreleasepool {
RevanPerson *person5 = [[[RevanPerson alloc] init] autorelease];
RevanPerson *person6 = [[[RevanPerson alloc] init] autorelease];
}
}
}
return 0;
}
- 打印输出
objc[62998]: ##############
objc[62998]: AUTORELEASE POOLS for thread 0x100392380
objc[62998]: 4 releases pending.
objc[62998]: [0x103002000] ................ PAGE (hot) (cold)//这是一个page
objc[62998]: [0x103002038] ################ POOL 0x103002038 //这个push时加入的POOL_BOUNDARY
objc[62998]: [0x103002040] 0x102804200 RevanPerson
objc[62998]: [0x103002048] 0x102804060 RevanPerson
objc[62998]: [0x103002050] 0x102804390 RevanPerson
objc[62998]: ##############
- 2、测试代码
#import
#import "RevanPerson.h"
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
RevanPerson *person1 = [[[RevanPerson alloc] init] autorelease];
RevanPerson *person2 = [[[RevanPerson alloc] init] autorelease];
RevanPerson *person3 = [[[RevanPerson alloc] init] autorelease];
@autoreleasepool {
for (int i = 0; i < 600; i++) {
RevanPerson *person4 = [[[RevanPerson alloc] init] autorelease];
}
_objc_autoreleasePoolPrint();
@autoreleasepool {
RevanPerson *person5 = [[[RevanPerson alloc] init] autorelease];
RevanPerson *person6 = [[[RevanPerson alloc] init] autorelease];
}
}
}
return 0;
}
- 打印输出
objc[63030]: ##############
objc[63030]: AUTORELEASE POOLS for thread 0x100392380
objc[63030]: 605 releases pending.
objc[63030]: [0x101006000] ................ PAGE (full) (cold)//第一个page对象的存储空间已经满了
objc[63030]: [0x101006038] ################ POOL 0x101006038 //第一个自动释放池push时加入的POOL_BOUNDARY
objc[63030]: [0x101006040] 0x100533660 RevanPerson
objc[63030]: [0x101006048] 0x1005334c0 RevanPerson
objc[63030]: [0x101006050] 0x100525040 RevanPerson
objc[63030]: [0x101006058] ################ POOL 0x101006058 //第二个自动释放池push时加入的POOL_BOUNDARY
objc[63030]: [0x101006060] 0x100526ec0 RevanPerson
objc[63030]: [0x101006068] 0x100525a00 RevanPerson
objc[63030]: [0x101006070] 0x100522ee0 RevanPerson
objc[63030]: [0x101006078] 0x100522380 RevanPerson
objc[63030]: [0x101006080] 0x1005218f0 RevanPerson
.
.
.
.
objc[63030]: [0x101006ff0] 0x100535f10 RevanPerson
objc[63030]: [0x101006ff8] 0x100535f20 RevanPerson
objc[63030]: [0x101008000] ................ PAGE (hot) //第一个page对象的存储空间已经满了,第二个page开始
objc[63030]: [0x101008038] 0x100535f30 RevanPerson
objc[63030]: [0x101008040] 0x100535f40 RevanPerson
七、小结
-
自动释放池
- __AtAutoreleasePool结构体调用push函数时,会将一个POOL_BOUNDARY入栈,并且返回其存放的地址
- __AtAutoreleasePool结构体调用pop函数时,传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY
-
autorelease
- 将对象自己加入到page中
-
AutoreleasePoolPage
- id *next指向了下一个能存放autorelease对象地址的区域