一、autoreleasepool源码解释
AutoreleasePool是一个堆栈,里面装着指针。那么栈的底层实现是什么呢?是数组。
AutoreleasePool全名叫NSAutoreleasePool。它就是一个对象引用计数自动处理器,在官方文档中被称为是一个类。
在ARC中,在遵守一些规则的情况下,可以自动释放对象。系统自动帮对象调用了autorelease方法,然后就会把对象扔进池里面,等一次runloop结束,这个池会被系统销毁,池里面的对象也就跟着被销毁了。
NSAutoreleasePool可以同时有多个,它的组织是个栈,总是存在一个栈顶pool,也就是当前pool,每创建一个pool,就往栈里压一个,改变当前pool为新建的pool,然后,每次给pool发送drain消息,就弹出栈顶的pool,改当前pool为栈里的下一个 pool。
跟MRC的release方法比较就是延迟了对象的销毁的时间。但autoreleasepool依然不是.Net/Java那种全自动的垃圾回收机制。
二、@autoreleasepool{} C++文件
@autoreleasepool
到底是什么?我们在命令行中使用 clang -rewrite-objc main.m
让编译器重新改写这个文件。
如果报错'UIKit/UIKit.h' file not found ,解决方法如下:
1.进入终端,键入命令 vim ~/.bash_profile
2.在vim界面输入i进入编辑编辑状态并且键入:alias rewriteoc='clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'
3.键入完毕,点esc退出编辑状态,再键入:wq退出vim并保存,执行source ~/.bash_profile<-这句一定要执行,执行才会生效
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk xxxxx.m
请参考:https://www.jianshu.com/p/43a09727eb2c
编译到的main.cpp文件主要代码如下:
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool};
@autoreleasepool是一个_AtAutoreleasePool的结构体。_AtAutoreleasePool的结构体里的方法如下:
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
autoreleasepool里是调用 栈结构的push() 和 pop() 操作。
三、autoreleasepool结构
综上两点得出autoreleasepool结构如下图:
autoreleasepool底层是由autoreleasepoolpage组成的双向链表结构。
四、autoreleasepoolpage组成
magic_t const magic; //用来校验 AutoreleasePoolPage 的结构是否完整
id *next;//指向最新添加的 autoreleased 对象的下一个位置,初始化时指向 begin()
pthread_t const thread;//指向当前线程
AutoreleasePoolPage * const parent;// 指向父结点,第一个结点的 parent 值为 nil
AutoreleasePoolPage *child;//指向父结点,第一个结点的 parent 值为 nil
uint32_t const depth;//链表的深度,节点个数
uint32_t hiwat;//high water mark 数据容纳的一个上限
PAGE_MAX_SIZE;//size大小为4096,虚拟内存每个扇区4096个字节,4K对齐的说法
COUNT;//一个page里的对象数
POOL_BOUNDARY;//边界对象,以前为POOL_SENTINEL哨兵对象
五、autoreleasepool 运行过程
主要过程:
AutoreleasePoolPage::push()
AutoreleasePoolPage::autorelease((id)this);
AutoreleasePoolPage::pop(ctxt)
源码代码示例:
——————— push ——————
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
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);
return dest;
}
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();//coldpage()
if (page && !page->full()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);//完全没有page,重新创建一个page,并把对象加入page中
}
}
id *add(id obj)
{
id *ret = next; // faster than `return next-1` because of aliasing
*next ++ = obj;
return ret;
}
—————— autorelease ——————
// Replaced by ObjectAlloc
- (id)autorelease {
return ((id)self)->rootAutorelease();
}
__attribute__((noinline,used))
id
objc_object::rootAutorelease2()
{
assert(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
static inline id autorelease(id obj)
{
id *dest __unused = autoreleaseFast(obj);
assert(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}
autoreleaseFast()
——————— pop ——————
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
page = pageForPointer(token);
stop = (id *)token;
page->releaseUntil(stop);
}
void releaseUntil(id *stop)
{
while (this->next != stop) {
AutoreleasePoolPage *page = hotPage();
id obj = *--page->next;
if (obj != POOL_BOUNDARY) {
objc_release(obj);
}
}
}
__attribute__((aligned(16)))
void
objc_release(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
return obj->release();
}
inline void
objc_object::release()
{
assert(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
rootRelease();
return;
}
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_release);
}
六、验证
1.下载源码 https://github.com/RetVal/objc-runtime
1.1 运行步骤
选择debug-objc文件里的main.m
选择debug-objc的syboml ,My-Mac
1.2 如果下载下来运行不了参考 https://blog.youkuaiyun.com/wotors/article/details/52489464
2.查看验证page的内容。在源码的main.m里输入一个字符串,打上断点:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString * s1 = [NSString stringWithFormat:@"8pmedu28pmedu28pmedu--S1"];
}
return 0;
}
3.在源码main.m里使用以下命令,查看当前page页内容
expr AutoreleasePoolPage::hotPage()
(lldb) expr AutoreleasePoolPage::hotPage()
((anonymous namespace)::AutoreleasePoolPage *) $1 = 0x0000000102803000
p *$1
p *$1
((anonymous namespace)::AutoreleasePoolPage) $2 = {
magic = {
m = ([0] = 2711724449, [1] = 1330926913, [2] = 1162626386, [3] = 558191425)
}
next = 0x0000000102803048
thread = 0x00000001009ba380
parent = 0x0000000000000000
child = 0x0000000000000000
depth = 0
hiwat = 0
}
p $1.printAll()
(lldb) p $1.printAll()
objc[1756]: ##############
objc[1756]: AUTORELEASE POOLS for thread 0x1009ba380
objc[1756]: 2 releases pending.
objc[1756]: [0x102803000] ................ PAGE (hot) (cold)
objc[1756]: [0x102803038] ################ POOL 0x102803038
objc[1756]: [0x102803040] 0x100c37fc0 __NSCFString
objc[1756]: ##############
Fix-it applied, fixed expression was:
$1->printAll()
验证 POOL 0x102803038的内存地址a.打印出next[-1] pool_boundary所在的值
p $0.next[-2]
(id) $11 = nil
Fix-it applied, fixed expression was:
$0->next[-2]
b.取地址符
p &$11
(id *) $12 = 0x000000010100a038
c.比较b的结果和p $1.printAll()里POOL 的地址值。