iOS底层原理 - 内存管理 之 autorelease

2019-10-22  本文已影响0人  hazydream

面试题引发的思考:

Q: 自动释放池autoreleasepool底层原理介绍:

Q: AutoreleasePoolPage为何设置成双向链表结构?


1. autoreleasepool分析

// TODO: -----------------  Person类  -----------------
@interface Person : NSObject
@end

@implementation Person
- (void)dealloc {
    NSLog(@"%s", __func__);
    [super dealloc];
}
@end

// TODO: -----------------  main  -----------------
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[[Person alloc] init] autorelease];
    }
    return 0;
}

// 打印结果
Demo[1234:567890] -[Person dealloc]

将OC代码转换为C\C++代码,使用xcrun:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件

main函数

__AtAutoreleasePool底层结构为:

__AtAutoreleasePool结构体

objc_autoreleasePoolPushobjc_autoreleasePoolPop底层结构为:

objc_autoreleasePoolPush、objc_autoreleasePoolPop

由OC源码可知:

objc_autoreleasePoolPushobjc_autoreleasePoolPop底层分别调用AutoreleasePoolPagepush()方法和pop()方法。

由以上分析可知:

@autoreleasepool {
    Person *person = [[[Person alloc] init] autorelease];
}

#pragma mark - 将以上OC代码转换为C\C++代码为:
{
    __AtAutoreleasePool __autoreleasepool;

    Person *person = [[[Person alloc] init] autorelease];
}

#pragma mark - 进一步转换为C\C++代码为:
{
    atautoreleasepoolobj = objc_autoreleasePoolPush();

    Person *person = [[[Person alloc] init] autorelease];

    objc_autoreleasePoolPop(atautoreleasepoolobj);
}

由此得出结论:

@autoreleasepool {
    // 其他代码
}

#pragma mark - 转换为C\C++代码为:
{
    atautoreleasepoolobj = objc_autoreleasePoolPush();
    // 其他代码
    objc_autoreleasePoolPop(atautoreleasepoolobj);
}

由以上步骤总结可知:

  • 自动释放池的主要底层数据结构是:__AtAutoreleasePoolAutoreleasePoolPage
  • 调用了autorelease的对象最终都是通过AutoreleasePoolPage对象来管理的。

2. AutoreleasePoolPage分析

OC源码可知:

AutoreleasePoolPage主要构成
  • 每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址;

  • 所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起。

  • 双向链表是通过parent指针和child指针联系在一起的:
    parent指针指向上一个AutoreleasePoolPage对象的地址值;
    child指针指向下一个AutoreleasePoolPage对象的地址值。

AutoreleasePoolPage双向链表结构如下:

AutoreleasePoolPage双向链表结构 begin()、end()
  • begin()this指针 + this大小,即0x1000 + 7 * 8 = 0x1038
  • end()this指针 + SIZE,即0x1000 + 4096 = 0x2000

3. 方法分析

(1) push方法分析

push方法

由源码分析可知:

调用push方法会将一个POOL_BOUNDARY入栈,并且返回其存放的内存地址。


(2) autorelease方法分析

autorelease方法

由源码分析可知:

调用autorelease方法会将对象地址值入栈。


(3) pop方法分析

pop方法

由源码分析可知:

调用pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY


(4) 总结可知

  • 调用push方法会将一个POOL_BOUNDARY入栈,并且返回其存放的内存地址;
  • 调用autorelease方法会将对象地址值入栈;
  • 调用pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY
  • id *next指向了下一个能存放autorelease对象地址的区域。

(5) 实例分析:

// TODO: -----------------  main  -----------------
int main(int argc, const char * argv[]) {
    @autoreleasepool { // 位置一:r1 = push()
        Person *p1 = [[[Person alloc] init] autorelease];
        Person *p2 = [[[Person alloc] init] autorelease];
        
        @autoreleasepool { // 位置二:r2 = push()
            Person *p3 = [[[Person alloc] init] autorelease];
            
            @autoreleasepool { // 位置三:r3 = push()
                Person *p4 = [[[Person alloc] init] autorelease];
            } // 位置四:pop(r3)
            
        } // 位置五:pop(r2)
        
    } // 位置六:pop(r1)
    return 0;
}

以上对象在AutoreleasePoolPage的内存结构如下:

AutoreleasePoolPage内存结构

4. 自动释放池查看

可以通过以下私有函数来查看自动释放池的情况
extern void _objc_autoreleasePoolPrint(void);

// TODO: -----------------  main  -----------------
extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool { // r1 = push()
        // 位置一:_objc_autoreleasePoolPrint();
        Person *p1 = [[[Person alloc] init] autorelease];
        Person *p2 = [[[Person alloc] init] autorelease];

        // 位置二:_objc_autoreleasePoolPrint();
        @autoreleasepool { // r2 = push()
            Person *p3 = [[[Person alloc] init] autorelease];
            
            // 位置三:_objc_autoreleasePoolPrint();
            @autoreleasepool { // r3 = push()
                Person *p4 = [[[Person alloc] init] autorelease];
                
                // 位置四:_objc_autoreleasePoolPrint();
            } // pop(r3)
            
            // 位置五:_objc_autoreleasePoolPrint();
        } // pop(r2)
        
        // 位置六:_objc_autoreleasePoolPrint();
    } // pop(r1)
    
    // 位置七:_objc_autoreleasePoolPrint();
    return 0;
}

语句_objc_autoreleasePoolPrint();在不同位置调用打印结果如下:

位置一打印结果 位置二打印结果 位置三打印结果 位置四打印结果 位置五打印结果 位置六打印结果 位置七打印结果
上一篇 下一篇

猜你喜欢

热点阅读