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

面试题引发的思考:

Q: ARC都帮我们做了什么?

  • ARC是 LLVM编译器 和 Runtime系统 相互协作的一个结果。

Q: 谈一谈weak指针的实现原理。

  • 利用 哈希表weak_tableweak指针 与 被指向的对象 进行标记、关联;
  • 当对象销毁释放内存时,通过 标记 对 weak指针地址 进行查找,把 weak指针 逐个置为nil

Q: 指针类型的区别?

  • __strong:对对象进行retain
  • __weak:不会对对象进行retain,当对象销毁时,会自动指向nil
  • __unsafe_unretained:不会对对象进行retain,当对象销毁时,依然指向之前的内存空间(野指针)。

1. 案例分析

(1) 案例一

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

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

// TODO: -----------------  ViewController类  -----------------
- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"begin");
    {
        // 强指针person 指向 Person对象
        Person *person = [[Person alloc] init];
    }
    NSLog(@"end");
}

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

由以上代码可知:

强指针person是大括号内部局部变量,大括号执行结束后person会被销毁,此时没有指针指向Person对象,Person对象会被释放,打印结果可以验证。

(2) 案例二

// TODO: -----------------  ViewController类  -----------------
- (void)viewDidLoad {
    [super viewDidLoad];
    // 强指针
    __strong Person *person1;

    NSLog(@"begin");
    {
        Person *person = [[Person alloc] init];
        // 强指针person1 指向 Person的对象
        person1 = person;
    }
    NSLog(@"end - %@", person1);
}

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

由以上代码可知:

强指针person1指向Person的对象,viewDidLoad执行结束后以后person1才会销毁,此时没有指针指向Person对象,Person对象会被释放,打印结果可以验证。

(3) 案例三

// TODO: -----------------  ViewController类  -----------------
- (void)viewDidLoad {
    [super viewDidLoad];
    // 弱指针
    __weak Person *person2;

    NSLog(@"begin");
    {
        Person *person = [[Person alloc] init];
        person2 = person;
    }
    NSLog(@"end - %@", person2);
}

// 打印结果
Demo[1234:567890] begin
Demo[1234:567890] -[Person dealloc]
Demo[1234:567890] end - (null)

由以上代码可知:

弱指针person2指向Person的对象,大括号执行结束后person会被销毁,此时没有指针指向Person对象,Person对象会被释放,打印结果可以验证。

还可以发现:弱指针指向的对象销毁,弱指针的值会自动清空,所以person2打印结果为null

(4) 案例四

// TODO: -----------------  ViewController类  -----------------
- (void)viewDidLoad {
    [super viewDidLoad];
    // 不安全弱指针 - 野指针
    __unsafe_unretained Person *person3;

    NSLog(@"begin");
    {
        Person *person = [[Person alloc] init];
        person3 = person;
    }
    NSLog(@"end - %@", person3);
}

// 打印结果
Demo[1234:567890] begin
Demo[1234:567890] -[Person dealloc]
Demo[1234:567890] Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)

由以上代码可知:

不安全弱指针person3指向Person的对象,大括号执行结束后person会被销毁,此时没有指针指向Person对象,Person对象会被释放,打印结果可以验证。

还可以发现:不安全弱指针指向的对象销毁,不安全弱指针依然指向之前的内存空间,所以person3会导致坏地址访问。

(5) 总结

  • __strong:对对象进行retain
  • __weak:不会对对象进行retain,当对象销毁时,会自动指向nil
  • __unsafe_unretained:不会对对象进行retain,当对象销毁时,依然指向之前的内存空间(野指针)。

2. 源码分析

(1) dealloc方法

由OC源码查找dealloc方法:

iOS底层原理 - 内存管理 之 weak_第1张图片
dealloc函数分析

由OC源码可知:

调用dealloc方法,会清除成员变量,移除关联对象,并将指向当前对象的弱指针置为nil

(2) 弱指针置为nil的具体操作

由iOS底层原理 - 探寻Runtime本质(一)可知:

isa的结构中的信息has_sidetable_rc作用为:

  • 判断引用计数器是否过大无法存储在isa中;如果为1,那么引用计数会存储在一个叫SideTable的类的属性中。

属性SideTable结构如下:

iOS底层原理 - 内存管理 之 weak_第2张图片
SideTable结构

接下来跳到clearDeallocating方法,查看如何将指向当前对象的弱指针置为nil

iOS底层原理 - 内存管理 之 weak_第3张图片
弱指针处理

由OC源码可知:

  • 当一个对象objectweak指针指向时,这个weak指针会以object作为key,被存储到sideTable类的weak_table这个散列表上对应的一个weak指针数组里面。
  • 当一个对象objectdealloc方法被调用时,Runtime会以objectkey,从sideTableweak_table散列表中,找出对应的weak指针列表,然后将里面的weak指针逐个置为nil

你可能感兴趣的:(iOS底层原理 - 内存管理 之 weak)