如果您希望拥有更好的阅读体验,欢迎访问 我的开发笔记
前段时间离职了,现在终于有时间来了解些以前一直没有弄懂的知识了,啥也不说了,失业才是第一生产力,啊哈哈哈哈哈。。。。。
先把项目改为MRC,在main.m中写如下代码.
int main(int argc, const char * argv[]) {
@autoreleasepool {
__unused RTStudent *stu = [[[RTStudent alloc] init] autorelease];
}
return 0;
}
使用xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m转为C++代码,再整理后得
int main(int argc, const char * argv[]) {
{
__AtAutoreleasePool __autoreleasepool;
RTStudent *stu = objc_msgSend(objc_msgSend(objc_msgSend(objc_getClass("RTStudent"), sel_registerName("alloc")), sel_registerName("init")), sel_registerName("autorelease"));
}
return 0;
}
其中__AtAutoreleasePool是一个结构体:
struct __AtAutoreleasePool {
__AtAutoreleasePool() {
atautoreleasepoolobj = objc_autoreleasePoolPush();
} //构造函数
~__AtAutoreleasePool() {
objc_autoreleasePoolPop(atautoreleasepoolobj)
} //析构函数
void * atautoreleasepoolobj;
};
将objc_msgSend转为OC方法:
int main(int argc, const char * argv[]) {
{
__AtAutoreleasePool __autoreleasepool;
RTStudent *stu = [[[RTStudent alloc] init] autorelease];
}
return 0;
}
在main函数中可见@autoreleasepool的作用是在{}中声明了一个__AtAutoreleasePool __autoreleasepool结构体对象,会自动调用__AtAutoreleasePool的构造函数__AtAutoreleasePool(),就又调用了objc_autoreleasePoolPush()函数,当出了{}的作用域时,会销毁局部变量__autoreleasepool,此时就会调用析构函数~__AtAutoreleasePool(),然后就调用了objc_autoreleasePoolPop()函数,可见autorelease的对象,会自动销毁应该和push和pop有关了
那么push和pop到底做了哪些事情呢?
在NSObject.mm文件中找到objc_autoreleasePoolPush()和objc_autoreleasePoolPop()
void * objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
具体实现是调用了AutoreleasePoolPage类的push和pop方法,进入方法查看后,发现不论是push还是pop都主要和AutoreleasePoolPage对象在管理
class AutoreleasePoolPage
{
# define EMPTY_POOL_PLACEHOLDER ((id*)1)
# define POOL_BOUNDARY nil
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MAX_SIZE; // size and alignment, power of 2
#endif
static size_t const COUNT = SIZE / sizeof(id);
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
}
- 每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址
- 所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起
- next指向下一个可用于存放autorelease对象的地址

那么从哪个地址开始存放需要autorelease的对象呢?它有一个方法begin(),可以看到,this的起始地址+自己的成员所占的大小,就是存放需要autorelease的对象的起始地址
id * begin() {
return (id *) ((uint8_t *)this+sizeof(*this));
}
那么结束地址在哪里呢?它有一个方法end(),可以看到,this的起始地址+自己的数据结构的大小
id * end() {
return (id *) ((uint8_t *)this+SIZE);
}
由于一个page对象只有4096字节,所以当需要autorelease对象太多的话,一个page装不下,所以可能一个pool中有很多个page对象,而child指针指向的就是下一个page对象,parent指向的就是上一个page对象
push,autorelease,pop做了哪些事情呢?
- 调用push方法会将一个
POOL_BOUNDARY入栈,并且返回其存放的内存地址,也就是begin()的地址,并把这个地址返回给__AtAutoreleasePool对象的atautoreleasepoolobj,然后next指向下一个地址
static inline void *push()
{
id *dest;
if (DebugPoolAllocation) {
// Each autorelease pool starts on a new pool page.
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
dest = autoreleaseFast(POOL_BOUNDARY);
}
assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY); // POOL_BOUNDARY == *atautoreleasepoolobj
return dest;
}
- 一旦在自动释放池中,有对象调用了autorelease方法,就是将需要autorelease的对象放入page中,然后next指向下一个地址:
//调用了rootAutorelease()
- (id)autorelease {
return ((id)self)->rootAutorelease();
}
//这个方法里调用了rootAutorelease2函数
inline id objc_object::rootAutorelease() {
if (isTaggedPointer()) return (id)this;
if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
return rootAutorelease2();
}
//这个方法里调用了AutoreleasePoolPage的autorelease函数
__attribute__((noinline,used)) id objc_object::rootAutorelease2() {
assert(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
//这个方法里调用了page->add(obj) 将obj入栈
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 {
return autoreleaseNoPage(obj);
}
}
- 调用pop传递一个参数
atautoreleasepoolobj,就从最后一个入栈的autorelease对象开始调用release方法,直到遇到POOL_BOUNDARY这个地址,结束释放.
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
..........
page = pageForPointer(token);
stop = (id *)token;
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();
page->releaseUntil(stop);
.........
}
//释放
void releaseUntil(id *stop)
{
while (this->next != stop) {
AutoreleasePoolPage *page = hotPage();
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);
}
}
...........
}
打印自动释放池的信息
AutoreleasePoolPage类中有一个打印的方法
void _objc_autoreleasePoolPrint(void) {
AutoreleasePoolPage::printAll();
}
虽然_objc_autoreleasePoolPrint没有暴露出来,但可以使用extern void __objc_autoreleasePoolPrint(void);来声明,然后使用
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
__unused RTStudent *stu = [[[RTStudent alloc] init] autorelease];
__unused RTStudent *stu2 = [[[RTStudent alloc] init] autorelease];
@autoreleasepool {
__unused RTStudent *stu = [[[RTStudent alloc] init] autorelease];
@autoreleasepool {
__unused RTStudent *stu = [[[RTStudent alloc] init] autorelease];
__unused RTStudent *stu2 = [[[RTStudent alloc] init] autorelease];
_objc_autoreleasePoolPrint();
}
}
}
return 0;
}
objc[88455]: ##############
objc[88455]: AUTORELEASE POOLS for thread 0x100399380
objc[88455]: 8 releases pending.
objc[88455]: [0x100801000] ................ PAGE (hot) (cold)
objc[88455]: [0x100801038] ################ POOL 0x100801038
objc[88455]: [0x100801040] 0x10054f4a0 RTStudent
objc[88455]: [0x100801048] 0x100422ae0 RTStudent
objc[88455]: [0x100801050] ################ POOL 0x100801050
objc[88455]: [0x100801058] 0x100403100 RTStudent
objc[88455]: [0x100801060] ################ POOL 0x100801060
objc[88455]: [0x100801068] 0x100423740 RTStudent
objc[88455]: [0x100801070] 0x100405aa0 RTStudent
objc[88455]: ##############
可以看到,使用一次@autoreleasepool{},即创建一个__AtAutoreleasePool对象,就会调用一次push,放进一个POOL_BOUNDARY,page对象的next指针指向下一个地址,然后把需要autorelease对象放入page中,next指针移动相应数量的地址,当一个page没有满autorelease对象的时候,多个释放池会共用这个page,直到它满了,才会创建新的page对象
autorelease对象在什么时机会被调用release?
- 当对象被@autoreleasePool{}包裹的时候,在{}结束的时候,会被release
- (void)viewDidLoad {
[super viewDidLoad];
@autoreleasepool {
__unused RTPerson *obj = [[[RTPerson alloc] init] autorelease];
}
NSLog(@"%s",__func__);
}
//打印信息
2019-05-27 22:27:16.307421+0800 Test[91865:5115718] dealloc
2019-05-27 22:27:16.307560+0800 Test[91865:5115718] -[ViewController viewDidLoad]
- 当对象没有被@autoreleasePool{}包裹的时候,会在这一次runloop,休眠之前release
- (void)viewDidLoad {
[super viewDidLoad];
__unused RTPerson *obj = [[[RTPerson alloc] init] autorelease];
NSLog(@"%s",__func__);
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"%s",__func__);
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"%s",__func__);
}
//打印信息
2019-05-27 22:29:21.045179+0800 Test[91982:5120141] -[ViewController viewDidLoad]
2019-05-27 22:29:21.045454+0800 Test[91982:5120141] -[ViewController viewWillAppear:]
2019-05-27 22:29:21.048347+0800 Test[91982:5120141] dealloc
2019-05-27 22:29:21.058970+0800 Test[91982:5120141] -[ViewController viewDidAppear:]
打印当前的runloop的observers时,会发现有两个observe和AutoreleasePool相关
"<CFRunLoopObserver 0x6000037d0460 [0x108c9ab68]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x10b8871b1), context = <CFArray 0x6000008ee100 [0x108c9ab68]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7f8baa009058>\n)}}",
activities = 0x1 即 kCFRunLoopEntry = (1UL << 0) ,当runloop进入的时候,会调用回调,调用objc_autoreleasePoolPush()函数
"<CFRunLoopObserver 0x6000037d0280 [0x108c9ab68]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x10b8871b1), context = <CFArray 0x6000008ee100 [0x108c9ab68]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7f8baa009058>\n)}}"
kCFRunLoopBeforeWaiting = (1UL << 5) => 32
kCFRunLoopExit = (1UL << 7) => 128
activities = 0xa0 (十进制的160) 即 kCFRunLoopBeforeWaiting | kCFRunLoopExit
当runloop即将休眠和退出的时候调用回调,调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()函数
当runloop即将退出的时候调用回调,会调用objc_autoreleasePoolPop()函数
博主离职后有时间研究之前未弄懂的知识,先将项目改为MRC并在main.m中写代码。详细探究了自动释放池的原理,包括push、pop和autorelease的操作,AutoreleasePoolPage对象的内存管理,还介绍了打印自动释放池信息的方法,以及autorelease对象被调用release的时机。

2135

被折叠的 条评论
为什么被折叠?



