72.ARC中的 strong指针和weak指针

来源:互联网 发布:oracle表中添加数据 编辑:程序博客网 时间:2024/05/16 14:55

曾几何时, 自己也是对 strong/retain/weak等晕头转向, 今天看到了自己之前整理的关于ARC中的 strong指针和weak指针的 demo 和几篇文章, 所以便来总结一下.

简介

ARC是自iOS 5之后增加的新特性,完全消除了手动管理内存的烦琐,编译器会自动在适当的地方插入适当的retain、release、autorelease语句。你不再需要担心内存管理,因为编译器为你处理了一切
注意:ARC 是编译器特性,而不是 iOS 运行时特性(除了weak指针系统),它也不是类似于其它语言中的垃圾收集器。因此 ARC 和手动内存管理性能是一样的,有时还能更加快速,因为编译器还可以执行某些优化

原理

ARC 的规则非常简单:只要还有一个变量指向对象,对象就会保持在内存中。当指针指向新值,或者指针不再存在时,相关联的对象就会自动释放。这条规则对于成员变量、synthesize属性、局部变量都是适用的

strong指针和weak指针区别

strong 指针可以理解为对象的拥有者, weak 指针可以指向某个对象,但不属于对象的拥有者.
这里写图片描述
如上图面的关系图, 总结为以下几点:
1.strong指针和weak指针都指向同一个对象,但weak 指针不是拥有者
2.如果strong 指针指向另一个对象,则原先的对象就没有拥有者,就会被释放,此时 weak 指针会自动变成nil,称为空指针. 所以, weak型的指针变量自动变为nil是非常方便的,这样阻止了weak指针继续指向已释放对象,避免了野指针的产生,不然会导致非常难于寻找的Bug,空指针消除了类似的问题
3.weak指针主要用于“父-子”关系,父亲拥有一个儿子的strong指针,因此父亲是儿子的所有者;但为了阻止所有权循环,儿子需要使用weak指针指向父亲。典型例子是delegate模式,你的ViewController通过strong指针(self.view)拥有一个UITableView, UITableView的dataSource和delegate都是weak指针,指向你的ViewController

代码实践

为了使理解更清晰, 整理了一个小的 demo, 下面再来看一下. 先简单说下思路, ARC 自动管理对象的释放, 所以就涉及到一个自动释放池的概念, 这里就不赘述了. 在自动释放池内, 创建三组或强或弱的对比属性,创建时赋值, 打印. 然后在自动释放池外,即通过屏幕按钮点击事件实现, 再重新打印之前的所有属性, 再次对比, 验证以上说法.

第一次测试

#import "ViewController.h"@interface ViewController ()//第一组@property (nonatomic, strong) NSString *groupOne_strong;@property (nonatomic, weak) NSString *groupOne_weak;//第二组@property (nonatomic, strong) NSString *groupTwo_strong1;@property (nonatomic, strong) NSString *groupTwo_strong2;//第三组@property (nonatomic, strong) NSString *groupThree_strong;@property (nonatomic, weak) NSString *groupThree_weak;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    @autoreleasepool {        //正常的强引/弱引用对比        [self strongAndWeakCompare];        //强引用赋值给强引用,将第一个强引用置为 nil        [self strongGiveToStrong];        //强引用赋值给弱引用,将强引用置为 nil        [self strongGiveToWeak];    }}//正常的强引/弱引用对比- (void)strongAndWeakCompare{    self.groupOne_strong = [NSString stringWithFormat:@"强弱对比"];    self.groupOne_weak = [NSString stringWithFormat:@"strongAndWeakCompare"];    NSLog(@"第1组强弱对比:srong=%@-<%p>,weak=%@-<%p>",_groupOne_strong,_groupOne_strong,_groupOne_weak,_groupOne_weak);}//强引用赋值给强引用,将第一个强引用置为 nil- (void)strongGiveToStrong{    self.groupTwo_strong1 = [NSString stringWithFormat:@"强赋强"];    self.groupTwo_strong2 = _groupTwo_strong1;    _groupTwo_strong1 = nil;    NSLog(@"第2组强赋强:srong1=%@-<%p>,strong2=%@-<%p>",_groupTwo_strong1,_groupTwo_strong1,_groupTwo_strong2,_groupTwo_strong2);}//强引用赋值给弱引用,将强引用置为 nil- (void)strongGiveToWeak{    self.groupThree_strong = [NSString stringWithFormat:@"强赋弱"];    self.groupThree_weak = _groupThree_strong;    _groupThree_strong = nil;    NSLog(@"第3组强赋弱:srong=%@-<%p>,weak=%@-<%p>",_groupThree_strong,_groupThree_strong,_groupThree_weak,_groupThree_weak);}//待循环池中的对象销毁后,进行打印验证- (IBAction)click:(id)sender{    NSLog(@"第1组强弱对比:srong=%@-<%p>,weak=%@-<%p>",_groupOne_strong,_groupOne_strong,_groupOne_weak,_groupOne_weak);    NSLog(@"第2组强赋强:srong1=%@-<%p>,strong2=%@-<%p>",_groupTwo_strong1,_groupTwo_strong1,_groupTwo_strong2,_groupTwo_strong2);    NSLog(@"第3组强赋弱:srong=%@-<%p>,weak=%@-<%p>",_groupThree_strong,_groupThree_strong,_groupThree_weak,_groupThree_weak);}

PS:在自动释放池内还未释放时(这里也可以不写@autoreleasepool,ARC 会自动添加). 在这里这个自动释放池只是表明了一个对象的释放时机, MRC 中对象的释放时机更加灵活,但 weak 不可以在 MRC 情况下使用.

第一次打印结果

先看打印结果, 自动释放池内打印结果:

第1组强弱对比:srong=强弱对比-<0x7fe70945d320>, weak=strongAndWeakCompare-<0x7fe709456960>
第2组强赋强:srong1=(null)-<0x0>,strong2=强赋强-<0x7fe70945c460>
第3组强赋弱:srong=(null)-<0x0>,weak=强赋弱-<0x7fe709459750>

点击屏幕按钮, 自动释放池外打印结果:

第1组: 强弱对比:srong=强弱对比-<0x7fe70945d320>,weak=(null)-<0x0>
第2组: 强赋强:srong1=(null)-<0x0>,strong2=强赋强-<0x7fe70945c460>
第3组: 强赋弱:srong=(null)-<0x0>,weak=(null)-<0x0>

根据打印结果发现, 正如一开始我们所说的一样. 但是在第3组中 strong指针在置为 nil 后立即打印weak 指针, 其不为 nil, 这就是因为自动释放池中还没有销毁, 所以此时不会为 nil.

第二次测试

但我在整理的时候, 发现了一个问题, 一个 strong和 weak 的对比, 不会像刚刚这样, 难道是种特殊情况吗, 来看一下.

属性中添加第四组:

//第四组@property (nonatomic, strong)NSString *groupFour_strong;@property (nonatomic, weak)NSString *groupFour_weak;

自动释放池内执行以下代码:

 //强引用于常量区, 将强引用置为 nil- (void)constantStrongGiveToWeak{    self.groupFour_strong = @"常量区赋值";    //    self.groupFour_strong = [NSString stringWithFormat:@"a"];    self.groupFour_weak = _groupFour_strong;    _groupFour_strong = nil;    NSLog(@"第4组常量区赋值:srong=%@-<%p>,weak=%@-<%p>",_groupFour_strong,_groupFour_strong,_groupFour_weak,_groupFour_weak);}

自动释放池外点击事件中添加代码:

//待循环池中的对象销毁后,进行打印验证- (IBAction)click:(id)sender{    NSLog(@"第4组常量区赋值:srong=%@-<%p>,weak=%@-<%p>",_groupFour_strong,_groupFour_strong,_groupFour_weak,_groupFour_weak);}

第二次测试打印结果

来看一下打印结果 (自动释放池内/外):

第4组常量区赋值:srong=(null)-<0x0>,weak=常量区赋值-<0x107a55160>
第4组常量区赋值:srong=(null)-<0x0>,weak=常量区赋值-<0x107a55160>

这时, 会发现, 自动释放池外打印结果 weak 指针竟然不为 nil. 因为第一次测试中字符串的都是栈区创建的,所以也是在栈区来测试的, 若是直接赋值字符创,如 self.groupOne_strong = @”强弱对比”, 这样开辟的空间在常量区,所以即使将其置为 nil, 其也不会释放.

第三次测试(拓展)

添加属性, 声明特性为 weak 类型的 UIView

//声明关键字为 weak 的 View 作为成员变量@property (nonatomic, weak) UIView *red_weak_view;

自动释放池内执行以下代码:

//创建 weak View- (void)createWeakView{    UIView *temp_view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];    temp_view.backgroundColor = [UIColor redColor];    [self.view addSubview:temp_view];    self.red_weak_view = temp_view;}

自动释放池外点击事件中添加代码:

//待循环池中的对象销毁后,进行打印验证- (IBAction)click:(id)sender{    NSLog(@"redview=%@",_red_weak_view);}

第三次测试打印结果

自动释放池外打印结果:

redview=< UIView: 0x7fe63a4aeaf0; frame = (0 0; 100 100); layer = < CALayer: 0x7fe63a4a2650 > >

像上面这样直接创建的 view, 系统自动定义为 strong 类型的, 只是其作用域小. 当其addSubview后, 其引用计数 +1, 并不会释放, 所以属性中的weak view可以全局使用.
我曾见过有人这样使用, 但我并不明白其中的优势所在, 若大神懂得,望不吝赐教. 所以我在开发中会直接声明一个 strong 的属性, 如果忘记addSubview的话, 其便不能得到.

相关阅读: ARC下的注意事项及ARC官方文档翻译
参考资料: ARC指南1 - strong和weak指针

0 0
原创粉丝点击