block中如何避免循环引用

使用 weak–strong dance 技术

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

[objc] view plaincopy

  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)

[objc] view plaincopy

  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 的引用,从而打破了循环引用

时间: 2024-10-21 00:48:23

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

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

引言 使用block已经有一段时间了,感觉自己了解的还行,但是几天前看到CocoaChina上一个关于block的小测试主题 : [小测试]你真的知道blocks在Objective-C中是怎么工作的吗?,发现竟然做错了几道, 才知道自己想当然的理解是错误的,所以抽时间学习了下,并且通过一些测试代码进行测试,产生这篇博客. Block简介(copy一段) Block作为C语言的扩展,并不是高新技术,和其他语言的闭包或lambda表达式是一回事.需要注意的是由于Objective-C在iOS中不支

block的学习(block和timer的循环引用问题)

一.什么是回调函数? 回调函数,本质上也是个函数(搁置函数和方法的争议,就当这二者是一回事).由"声明"."实现"."调用"三部分组成. 在上面的例子中,我可以看出,函数amount(其实是Block),的声明和调用在A类中,而实现部分在B类中.也就是说,B类实现了amount函数,但并没有权限调用,最终还是 由A类触发调用.我们称这样的机制为"回调".意思是"虽然函数的实现写在B类中,但是真正的调用还是得由A类来完

OC中常见的循环引用总结

定义:循环引用可以简单理解为A引用了B,而B又引用了A,双方都同时保持对方的一个引用,导致任何时候引用计数都不为0,始终无法释放: 造成循环引用的三种情况 计时器NSTimer 原因分析: NSTimer变量通过target对xxxModel有强引用,NSTimer变量是xxxModel内部的成员变量,所以xxxModel对该成员变量有强引用  解决办法: 提供类似cleanTimer的公开方法,让外界主动调用释放NSTimer变量 block 原因分析: 某个类将block作为自己的属性变量,

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

http://blog.csdn.net/felix9/article/details/9619313 在实际开发中,发现使用Block有着比delegate和notification更简洁的优势.于是在目前的项目中大量的使用block. 在我的头文件我是这样声明使用block的. [plain] view plaincopy @interface BrushViewController : BaseViewController @property (nonatomic, copy) void 

Block的copy和循环引用的问题

在实际开发中,发现使用Block有着比delegate和notification更简洁的优势.于是在目前的项目中大量的使用block. 在我的头文件我是这样声明使用block的. 1 @interface BrushViewController : BaseViewController 2 3 @property (nonatomic, copy) void (^getCardInfo)(NSDictionary *cardInfo); 4 5 @end 我将block声明为copy的原因是在代

block的内存分析,循环引用,变量访问,数据结构定义

一.block的内存分析 如上图: 定义了一个weak的block,那么它在内存中的表现形式如右下角, 1.没有对block进行copy操作,而是weak,block就存储在栈空间中. 2.如果block存储于栈空间,不会对block内部所用到的对象产生强引用. 如上图: 对block进行了一次copy操作,如果对block进行copy操作,block就会存储到堆空间当中. 1.如果block存储于堆空间,就会对block内部所用到的对象产生强引用. 2.如下图所示,就会产生循环引用,造成P对象

block的用法和循环引用

一.block在OC中的用法可以分为大概一下几种. 1>用于成员属性,保存一段代码,可以替代代理传值. 比如说,创建一个ViewController控制器,点击屏幕就跳转到ModalViewController控制器里的时候,不用代理用block实现一些功能: // 在ModalViewController.h文件里声明: @property (nonatomic, strong) void(^valueBlock)(NSString *data); //在ModalViewController

swift中闭包的循环引用

首先我们先创造一个循环引用 var nameB:(()->())? override func viewDidLoad() { super.viewDidLoad() let bu = UIButton(type: .ContactAdd) bu.addTarget(self, action: "tap", forControlEvents: .TouchUpInside) view.addSubview(bu) run { print("name") sel

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

Block是一个很好用的东西,这篇文章主要来介绍:1.什么是Block?2.Block的使用?3.Block的循环引用问题及解决. 上面三点应该说是一个很大的问题,目前因为在做项目,我先仅就第三点做叙述,前两点等空闲的时候我再做补充. 1. 2. 3.Block的循环引用问题及解决. 首先我们需要明确的是,一个对象的Block属性是使用copy来修饰,当Block被copy时,会对block中用到的对象产生强引用(ARC)或者引用计数加一(MRC).当我们使用Block时,如果Block方法又引