面试题引发的思考:
Q: ARC都帮我们做了什么?
- ARC是 LLVM编译器 和 Runtime系统 相互协作的一个结果。
Q: 谈一谈weak
指针的实现原理。
- 利用 哈希表
weak_table
对weak
指针 与 被指向的对象 进行标记、关联; - 当对象销毁释放内存时,通过 标记 对
weak
指针地址 进行查找,把weak
指针 逐个置为nil
。
Q: 指针类型的区别?
__strong
:对对象进行retain
;__weak
:不会对对象进行retain
,当对象销毁时,会自动指向nil
;__unsafe_unretained
:不会对对象进行retain
,当对象销毁时,依然指向之前的内存空间(野指针)。
Q: dealloc
方法作用?
- 调用
dealloc
方法,会清除 成员变量,移除 关联对象,并将 指向当前对象的弱指针 置为nil
。
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 - <Person: 0x600003dc8640>
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
方法:
![](https://img.haomeiwen.com/i1319505/4f95957add86322e.png)
由OC源码可知:
调用
dealloc
方法,会清除成员变量,移除关联对象,并将指向当前对象的弱指针置为nil
。
(2) 弱指针置为nil
的具体操作
isa
的结构中的信息has_sidetable_rc
作用为:
- 判断引用计数器是否过大无法存储在
isa
中;如果为1,那么引用计数会存储在一个叫SideTable
的类的属性中。
属性SideTable
结构如下:
![](https://img.haomeiwen.com/i1319505/e7cee83741dcb94e.png)
接下来跳到clearDeallocating
方法,查看如何将指向当前对象的弱指针置为nil
:
![](https://img.haomeiwen.com/i1319505/ed72f2dcce104ffc.png)
由OC源码可知:
- 当一个对象
object
被weak
指针指向时,这个weak
指针会以object
作为key
,被存储到sideTable
类的weak_table
这个散列表上对应的一个weak
指针数组里面。- 当一个对象
object
的dealloc
方法被调用时,Runtime会以object
为key
,从sideTable
的weak_table
散列表中,找出对应的weak
指针列表,然后将里面的weak
指针逐个置为nil
。
网友评论