ios避免循环引用

翻译至http://masteringios.com/blog/2014/03/06/avoid-strong-reference-cycles/?utm_source=tuicool

ARC引入之后,内存管理变得很简单,但是即使不需要担心何时retain,release,依然有些规则去避免内存问题,本篇文章讨论关于循环引用的问题。

什么是循环强引用?比如说有两个对象,对象A 和对象 B,如果A强引用持有了B,B又强持有了A,这种情况下,就是循环强引用。我们会讨论2种通常会出现循环强引用的情形: blocks and delegates.

1. 代理 delegate

代理是OC中非常常用的模式,代理中,一个对象代表另一个对象执行相关操作。被代理对象持有一份代理对象的引用,当在合适的时机发送信息时,代理会通过改变UI进行响应。

API中得一个例子就是 UITableView 和它的 delegate. 这个示例中, table view 持有了一份代理的引用,代理对象持有了一份 table view的 引用,这就意味着相互引用,内存将无法释放。

如下自定义示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#import <Foundation/Foundation.h>
 
@class ClassA;
 
@protocol ClassA Delegate < NSObject >
 
-( void )classA:(ClassA *)classAObject didSomething:( NSString *)something;
 
@end
 
@interface ClassA : NSObject
 
@property (nonatomic, strong) id <ClassADelegate> delegate;
 
@end

这在ARC环境下会产生一个T retain cycle,要避免这个问题,我们需要改变代理的引用属性为weak。 

1
@property (nonatomic, weak) id <ClassADelegate> delegate;

weak引用不会造成持有关系,不会保持对象的生命。如果没有对象持有代理对象和被代理对象,首先代理对象会销毁,然后被代理对象的强引用会被释放。如果没有指针再指向被代理对象,被代理对象会释放。

2.块 Blocks

block是代码块,类似于C函数,除了可执行代码之外,可能包含栈或堆中的变量。block因此保存了一系列的数据,这样可以在需要执行的时候进行使用,因为blocks 保持了代码执行需要的数据,主要用于回调。 

Blocks 是 Objective C 对象, 但是有些内存管理原则只适用于  blocks, 不适于于Objective C 对象。

Blocks 保持了对所包含的对象的强引用,包括对象本身,所以很容易引起retain cycle. 如果一个对象包含一个属性block如下:

1
@property (copy) void (^block)( void );

在它的实现中,你有一个这样的方法:

1
2
3
4
5
6
7
- ( void )methodA {
 
     self .block = ^{
 
         [ self methodB];
     };
}

这样就产生了self的强循环引用,对象self持有了block的强引用,block又持有了self的强引用。

提示:针对block属性,使用copy比较好,因为block需要copy,来保持与原始内存的分离。

为了避免循环强引用,我们使用 weak属性. 以下是代码:

1
2
3
4
5
6
7
8
9
- ( void )methodA {
 
    __weak ClassB * weakSelf = self ;
 
     self .block = ^{
 
         [weakSelf methodB];
     };
}

通过对self的弱引用, block 不会保持与self的强关系,如果对象在block之前销毁,weakSelf 指针会被自动设置为nil,这样虽然很好的避免了内存问题,但是,指针是nil,我们的方法将不会被调用,block就不会实现所期望的行为,为了避免这种情况,我们将示例修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- ( void )methodA {
 
     __weak ClassB * weakSelf = self ;
 
     self .block = ^{
 
         __strong ClassB *strongSelf = weakSelf;
 
         if (strongSelf) {
 
             [strongSelf methodB];
         }
     };
}

我们在block内部创建了一个强持有的 self,这个引用会属于block保持,和block生命周期一样,它不会阻止 self 对象销毁,这样我们也避免了循环引用的问题。

不是所有的循环引用都像我上面的例子那么容易看出来,所有当你的block代码变得复杂的时候,你要考虑使用 weak 引用 。有2种情形容易出现循环引用,如上,通过使用weak 引用很容易打破循环引用,只要你能正确的识别出来,ARC虽然让内存管理便得容易了,但是我们仍然要对其保持警觉的思维。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值