iOS底层原理|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底层原理|iOS底层原理 - 内存管理 之 weak
文章图片
dealloc函数分析 由OC源码可知:
【iOS底层原理|iOS底层原理 - 内存管理 之 weak】调用dealloc方法,会清除成员变量,移除关联对象,并将指向当前对象的弱指针置为nil
(2) 弱指针置为nil的具体操作 由iOS底层原理 - 探寻Runtime本质(一)可知:
isa的结构中的信息has_sidetable_rc作用为:
  • 判断引用计数器是否过大无法存储在isa中;如果为1,那么引用计数会存储在一个叫SideTable的类的属性中。
属性SideTable结构如下:
iOS底层原理|iOS底层原理 - 内存管理 之 weak
文章图片
SideTable结构 接下来跳到clearDeallocating方法,查看如何将指向当前对象的弱指针置为nil
iOS底层原理|iOS底层原理 - 内存管理 之 weak
文章图片
弱指针处理 由OC源码可知:
  • 当一个对象objectweak指针指向时,这个weak指针会以object作为key,被存储到sideTable类的weak_table这个散列表上对应的一个weak指针数组里面。
  • 当一个对象objectdealloc方法被调用时,Runtime会以objectkey,从sideTableweak_table散列表中,找出对应的weak指针列表,然后将里面的weak指针逐个置为nil

    推荐阅读