使用 weak–strong dance 技术

block 可以直接引用 self,但是要非常小心地在 block 中引用 self。因为在 block 引用 self,可能会导致循环引用。如下例所示:

  1. @interface KSViewController ()
  2. {
  3. id _observer;
  4. }
  5. @end
  6. @implementation KSViewController
  7. - (void)viewDidLoad
  8. {
  9. [super viewDidLoad];
  10. // Do any additional setup after loading the view, typically from a nib.
  11. KSTester * tester = [[KSTester alloc] init];
  12. [tester run];
  13. _observer = [[NSNotificationCenter defaultCenter]
  14. addObserverForName:@"TestNotificationKey"
  15. object:nil queue:nil usingBlock:^(NSNotification *n) {
  16. NSLog(@"%@", self);
  17. }];
  18. }
  19. - (void)dealloc
  20. {
  21. if (_observer) {
  22. [[NSNotificationCenter defaultCenter] removeObserver:_observer];
  23. }
  24. }

在上面代码中,我们添加向通知中心注册了一个观察者,然后在 dealloc 时解除该注册,一切看起来正常。但这里有两个问题:
    1.) 在消息通知 block 中引用到了 self,在这里 self 对象被 block retain,而 _observer 又
retain 该 block的一份拷贝,通知中心又持有 _observer。因此只要 _observer 对象还没有被解除注册,block
就会一直被通知中心持有,从而 self 就不会被释放,其 dealloc 就不会被调用。而我们却又期望在 dealloc 中通过
removeObserver 来解除注册以消除通知中心对 _observer/block 的 retain。
    2.) 同时,_observer 是在 self 所在类中定义赋值,因此是被 self retain 的,这样就形成了循环引用。
上面的过程 1) 值得深入分析一下:
苹果官方文档中对 addObserverForName:object:queue:usingBlock: 中的 block 变量说明如下:

The block is copied by the notification center and (the copy) held until the observer registration is removed.
因此,通知中心会拷贝 block 并持有该拷贝直到解除 _observer 的注册。在 ARC 中,在被拷贝的 block 中无论是直接引用 self 还是通过引用 self 的成员变量间接引用 self,该 block 都会 retain self。
这两个问题,可以用 weak–strong dance 技术来解决。该技术在 WWDC 中介绍过:2011 WWDC Session #322 (Objective-C Advancements in Depth)

  1. __weak KSViewController * wself = self;
  2. _observer = [[NSNotificationCenter defaultCenter]
  3. addObserverForName:@"TestNotificationKey"
  4. object:nil queue:nil usingBlock:^(NSNotification *n) {
  5. KSViewController * sself = wself;
  6. if (sself) {
  7. NSLog(@"%@", sself);
  8. }
  9. else {
  10. NSLog(@"<self> dealloc before we could run this code.");
  11. }
  12. }];

下面来分析为什么该手法能够起作用。
    首先,在 block 之前定义对 self 的一个弱引用 wself,因为是弱引用,所以当 self 被释放时 wself 会变为 nil;然后在 block 中引用该弱应用,考虑到多线程情况,通过使用强引用 self 来引用该弱引用,这时如果 self 不为 nil 就会 retain self,以防止在后面的使用过程中 self 被释放;然后在之后的 block 块中使用该强引用 self,注意在使用前要对 self 进行了 nil
检测,因为多线程环境下在用弱引用 wself 对强引用 sself 赋值时,弱引用 wself 可能已经为 nil 了。
    通过这种手法,block 就不会持有 self 的引用,从而打破了循环引用

block中如何避免循环引用的更多相关文章

  1. block(六)循环引用-b

    在ARC与非ARC环境下对block使用不当都会引起循环引用问题,一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身,简单说就是self.theBloc ...

  2. block使用小结、在arc中使用block、如何防止循环引用

    引言 使用block已经有一段时间了,感觉自己了解的还行,但是几天前看到CocoaChina上一个关于block的小测试主题: [小测试]你真的知道blocks在Objective-C中是怎么工作的吗 ...

  3. 关于Block的copy和循环引用的问题

    http://blog.csdn.net/felix9/article/details/9619313 在实际开发中,发现使用Block有着比delegate和notification更简洁的优势.于 ...

  4. Block的copy和循环引用的问题

    在实际开发中,发现使用Block有着比delegate和notification更简洁的优势.于是在目前的项目中大量的使用block. 在我的头文件我是这样声明使用block的. 我将block声明为 ...

  5. Block的使用及循环引用的解决

    Block是一个很好用的东西,这篇文章主要来介绍:1.什么是Block?2.Block的使用?3.Block的循环引用问题及解决. 1.什么是Block? 说这个问题之前,我先来说一下闭包(Closu ...

  6. block的用法和循环引用

    一.block在OC中的用法可以分为大概一下几种. 1>用于成员属性,保存一段代码,可以替代代理传值. 比如说,创建一个ViewController控制器,点击屏幕就跳转到ModalViewCo ...

  7. iOS - Block产生Memory Leaks循环引用导致的内存泄漏以及解决方案

    在ARC(自动引用技术)前,Objective-c都是手动来分配释放 释放 计数内存,其过程非常复杂. ARC技术推出后,貌似世界和平了很多,但是其实ARC并不等同于Java或者C#中的垃圾回收,AR ...

  8. swift中闭包的循环引用

    首先我们先创造一个循环引用 var nameB:(()->())? override func viewDidLoad() { super.viewDidLoad() let bu = UIBu ...

  9. 小解系列-自关联对象.Net MVC中 json序列化循环引用问题

    自关联对象在实际开发中用的还是比较多,例如常见的树形菜单.本文是自己实际的一个小测试,可以解决循环引用对象的json序列化问题,文笔不好请多见谅,如有错误请指出,希望有更好的解决方案,一起进步. 构造 ...

随机推荐

  1. BZOJ1828 [Usaco2010 Mar]balloc 农场分配

    直接贪心,我们把线段按照右端点从小到大排序,然后一个个尝试插入即可... 来证明贪心的正确性: 不妨设贪心得到的答案集合为$S$,最优解的答案集合为$T$ 若$S$不是最优解,那么$S \not= T ...

  2. 数位DP入门

    HDU 2089 不要62 DESC: 问l, r范围内的没有4和相邻62的数有多少个. #include <stdio.h> #include <string.h> #inc ...

  3. Msbuild项目集成右键菜单编译

    DS1.背景:   我们为什么要将VS2008命令行编译.sln文件集成到右键菜单呢? 原因一:VS2008很好很强大,但太费系统资源了,尤其是在虚拟机在里面装VS2008的时候更是如此. 原因二:有 ...

  4. pom 详解

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  5. Ubuntu安装node

    #!/bin/bash echo "添加环境变量需要root权限,如无root权限,则不添加环境变量" echo "输入Node下载地址(目前仅支持Node官方网站上Li ...

  6. 面向对象的JavaScript

    多态 多态背后的思想是将"做什么"和"谁去做,怎样去做"分离开来,也就是将"不变的事物"与"可变的事物"分离开来. 其最 ...

  7. wpf初步-grid布局-连连看棋盘

    private void Window_Loaded_1(object sender, RoutedEventArgs e) { //Button btn1 = new Button(); //btn ...

  8. HDU 1005 Number Sequence

    Number Sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)T ...

  9. redis清空缓存

    进入redis命令行 首先启动redis服务 redis-server /home/redis/redis_7901.conf redis-cli -p 7901(指定进入端口号为7901的redis ...

  10. Dojo的subscribe和publish的简单使用

    问题描述: 假设在你的页面有一个对话框,对话框中包含FilteringSelect这样的需要store的控件,在打开对话框时需要对这个控件做两件事情,一是给该控件的store填充数据,二是要给该控件设 ...