block中避免循环引用的问题

在ARC与非ARC环境下对block使用不当都会引起循环引用问题,一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身,简单说就是self.theBlock = ^(void){[self dosomething];或者self.otherVar = XXX;或者_otherVar = ...};block的这种循环引用会被编译器捕捉到并及时提醒,那要如何避免呢,我在这里做了个demo测试。


直接看demo
#import "ViewController.h"
#define CJSUPARC 1
typedef void(^myBlock)(void);

  @interface ViewController ()
  @property (nonatomic, copy)myBlock theBlock;
  @end

  @implementation ViewController

  - (void)dealloc
  {
  #if CJSUPARC
      [super dealloc];
  #else

  #endif
      NSLog(@"ViewController dealloc");
  }

  - (void)viewDidLoad {
      [super viewDidLoad];

  #if CJSUPARC
      __block typeof(self) wSelf = self;
      NSLog(@"%@",@(self.retainCount));
      myBlock block = ^(void){
          NSLog(@"%@",@(wSelf.retainCount));
          NSLog(@"%@.block init",[wSelf.class description]);
      };
      self.theBlock = [[block copy] autorelease];

      if (self.theBlock) {
          self.theBlock();
      }
  #else
  //    __block typeof(self) wSelf = self;
  //    __unsafe_unretained typeof(self) wSelf = self;
      __weak typeof(self) wSelf = self;
      myBlock block = ^(void){
          NSLog(@"%@.block init",[wSelf.class description]);
      };
      self.theBlock = [block copy];

      if (self.theBlock) {
          self.theBlock();
      }
  #endif

  }
  @end

  • MRC模式下运行
    2016-01-09 23:50:33.993 CJBlockDemo[52536:2193676] self.retainCount = 6
    2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] self.retainCount = 6
    2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] ViewController.block init
    2016-01-09 23:50:40.437 CJBlockDemo[52536:2193676] ViewController dealloc
    可以看到使用__block能够避免引起循环引用的问题

  • ARC模式下运行

使用__block

  2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] ViewController.block init
 虽然编译器警告是没有了,但ViewController却没有执行dealloc函数,说明在arc中__block还是会引起retain。

使用__unsafe_unretained

使用__weak

  2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] ViewController.block init
  2016-01-09 23:50:40.437 CJBlockDemo[52536:2193676] ViewController dealloc

都可以避免循环引用的问题,但由于前者是unsafe的,会造成野指针问题,所以尽量少用unsafe_unretained关键字


另外在多线程环境下(block中的wSelf有可能被析构的情况下),需要先将self转为strong指针,避免在运行到某个关键步骤时self对象被析构。
可参考AFNetworking代码:

__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback =  ^(AFNetworkReachabilityStatus status) { 
    __strong __typeof(weakSelf)strongSelf = weakSelf;
    strongSelf.networkReachabilityStatus = status;
    if (strongSelf.networkReachabilityStatusBlock) {
         strongSelf.networkReachabilityStatusBlock(status);
    }
};
  • 第一行:__weak __typeof(self)weakSelf = self;
    为防止callback内部对self强引用,weak一下。
    其中用到了__typeof(self),这里涉及几个知识点:
  1. __typeof、__typeof__、typeof的区别
    恩~~他们没有区别,但是这牵扯一段往事,在早期C语言中没有typeof这个关键字,__typeof、__typeof__是在C语言的扩展关键字的时候出现的。
    typeof是现代GNU C++的关键字,从Objective-C的根源说,他其实来自于C语言,所以AFNetworking使用了继承自C的关键字。
  2. 对于老的LLVM编译器上面这句话会编译报错,所以在很早的ARC使用者中流行__typeof(&*self)这种写法,原因如下
    大致说法是老LLVM编译器会将__typeof转义为 XXX类名 *const __strong的__strong和前面的__weak关键字对指针的修饰又冲突了,所以加上&*对指针的修饰。
  • 第三行:__strong __typeof(weakSelf)strongSelf = weakSelf;
    self转回strong了,这里__typeof()里面写的是weakSelf,里面写self也没有问题,因为typeof是编译时确定变量类型,所以这里写self 不会被循环引用。

  • 第四、五、六行,如果不转成strongSelf而使用weakSelf,后面几句话中,有可能在第四句执行之后self的对象可能被析构掉,然后后面的StausBlock没有执行,导致逻辑错误。

  • 最后第五行,使用前对block判空。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容