上一篇已经讲解了MRC与ARC的基本知识,本篇我们讲解MRC-block与ARC-block的基本内容。

在MRC时代,Block会隐式地对进入其作用域内的对象(或者说被Block捕获的指针指向的对象)加retain,来确保Block使用到该对象时,能够正确的访问。

这件事情在下面代码展示的情况中要更加额外小心。

MyViewController *myController = [[MyViewController alloc] init…];

// 隐式地调用[myController retain];造成循环引用

myController.completionHandler =  ^(NSInteger result) {

[myController dismissViewControllerAnimated:YES completion:nil];

};

[self presentViewController:myController animated:YES completion:^{

[myController release]; // 注意,这里调用[myController release];是在MRC中的一个常规写法,并不能解决上面循环引用的问题

}];

在这段代码中,myController的completionHandler调用了myController的方法[dismissViewController...],这时completionHandler会对myController做retain操作。而我们知道,myController对completionHandler也至少有一个retain(一般准确讲是copy),这时就出现了在内存管理中最糟糕的情况:循环引用!简单点说就是:myController retain了completionHandler,而completionHandler也retain了myController。循环引用导致了myController和completionHandler最终都不能被释放。我们在delegate关系中,对delegate指针用weak就是为了避免这种问题。

不过好在,编译器会及时地给我们一个警告,提醒我们可能会发生这类型的问题:

对这种情况,我们一般用如下方法解决:给要进入Block的指针加一个__block修饰符。

这个__block在MRC时代有两个作用:

•说明变量可改

•说明指针指向的对象不做这个隐式的retain操作

一个变量如果不加__block,是不能在Block里面修改的,不过这里有一个例外:static的变量和全局变量不需要加__block就可以在Block中修改。

MyViewController * __block myController = [[MyViewController alloc] init…];

// ...

myController.completionHandler =  ^(NSInteger result) {

[myController dismissViewControllerAnimated:YES completion:nil];

};

//之后正常的release或者retain

在ARC引入后,没有了retain和release等操作,情况也发生了改变:在任何情况下,__block修饰符的作用只有上面的第一条:说明变量可改。即使加上了__block修饰符,一个被block捕获的强引用也依然是一个强引用。这样在ARC下,如果我们还按照MRC下的写法,completionHandler对myController有一个强引用,而myController对completionHandler有一个强引用,这依然是循环引用,没有解决问题:(

于是我们还需要对原代码做修改。简单的情况我们可以这样写:

__block MyViewController * myController = [[MyViewController alloc] init…];

// ...

myController.completionHandler =  ^(NSInteger result) {

[myController dismissViewControllerAnimated:YES completion:nil];

myController = nil;  // 注意这里,保证了block结束myController强引用的解除

};

在completionHandler之后将myController指针置nil,保证了completionHandler对myController强引用的解除,不过也同时解除了myController对myController对象的强引用。这种方法过于简单粗暴了,在大多数情况下,我们有更好的方法。

这个更好的方法就是使用weak。(或者为了考虑iOS4的兼容性用unsafe_unretained,具体用法和weak相同,考虑到现在iOS4设备可能已经绝迹了,这里就不讲这个方法了)(关于这个方法的本质我们后面会谈到)

为了保证completionHandler这个Block对myController没有强引用,我们可以定义一个临时的弱引用weakMyViewController来指向原myController的对象,并把这个弱引用传入到Block内,这样就保证了Block对myController持有的是一个弱引用,而不是一个强引用。如此,我们继续修改代码:

MyViewController *myController = [[MyViewController alloc] init…];

// ...

MyViewController * __weak weakMyViewController = myController;

myController.completionHandler =  ^(NSInteger result) {

[weakMyViewController dismissViewControllerAnimated:YES completion:nil];

};

这样循环引用的问题就解决了,但是却不幸地引入了一个新的问题:由于传入completionHandler的是一个弱引用,那么当myController指向的对象在completionHandler被调用前释放,那么completionHandler就不能正常的运作了。在一般的单线程环境中,这种问题出现的可能性不大,但是到了多线程环境,就很不好说了,所以我们需要继续完善这个方法。

为了保证在Block内能够访问到正确的myController,我们在block内新定义一个强引用strongMyController来指向weakMyController指向的对象,这样多了一个强引用,就能保证这个myController对象不会在completionHandler被调用前释放掉了。于是,我们对代码再次做出修改:

MyViewController *myController = [[MyViewController alloc] init…];

// ...

MyViewController * __weak weakMyController = myController;

myController.completionHandler =  ^(NSInteger result) {

MyViewController *strongMyController = weakMyController;

  if (strongMyController) {

// ...

[strongMyController dismissViewControllerAnimated:YES completion:nil];

// ...

}

else {

// Probably nothing...

}

};

到此,一个完善的解决方案就完成了:)

以上就是block在ARC与MRC的用处,希望对大家有所帮助!!!

随机推荐

  1. google bookmarket api

    引用: 最近做了google书签同步的模块,发现google并没有公开bookmark相关的api接口,在网上也找了些资料,通过自己抓包分析,测试,总结下使用bookmark接口的心得,我是在andr ...

  2. 非常出色的一款WinForm窗体重绘GUI类库源码

    基本控件的演示 ScrollBar滚动条 各种圆形进度条 ProgressBar进度条 Mdi演示,仿谷歌浏览器 多种皮肤可供选择 一套专业级别的GUI控件,目前包含了窗体.进度条.滚动条以及MDI多 ...

  3. Class.forName()数据库驱动

    在学习jdbc中,用到Class.forName(驱动);,当时学习的时候知道Class.forName就是加载一个类到虚拟机,在加载一个类的时候,这个类的信息会被放到一个方法区,一个CLass 在J ...

  4. 简单RTP发送类c++实现

    我之前编译了jrtplib 3.9.1,并且在项目中使用,结果发现在用这个库时,程序体积有增加了300多K,感觉实在是有点笨重,我无法就是用来发送rtp包而已.想想还是自己重新实现一个简单的类用用拉倒 ...

  5. android:由URL载入中ImageView

    两种方法: 方法一: Bitmap bimage= getBitmapFromURL(bannerpath); image.setImageBitmap(bimage); public static ...

  6. 简单介绍如何使用robotium进行自动化测试

    1.      创建一个简单的工程()作为被测程序,运行后显示如下界面: 2.      创建一个Test Project 1).     打开eclipse,选择File->New->P ...

  7. linkin大话数据结构--数组

    数组概述:如何正确理解数组?数组也是一种类型 数组是多个相同类型数据的组合,实现对这些数据的统一管理.数组属引用类型,数组型数据是对象(Object),数组中的每个元素相当于该对象的成员变量数组中的元 ...

  8. 芝麻HTTP:TXT文本存储

    将数据保存到TXT文本的操作非常简单,而且TXT文本几乎兼容任何平台,但是这有个缺点,那就是不利于检索.所以如果对检索和数据结构要求不高,追求方便第一的话,可以采用TXT文本存储.本节中,我们就来看下 ...

  9. Python __new__ 实现单例模式 python经典面试题

    话不多说,上代码 class Singleton(object): def __new__(cls, *args, **kwargs): if not hasattr(cls, '_instance' ...

  10. CSS 控制鼠标在元素停留的样式

    以下资料来自网络,收藏学习总结用: 有时候需要改变鼠标样式,DIV 可以改成手型等,A也可以改成光标形式 巧合要用到鼠标样式效果,就顺便整理了下十五种CSS鼠标样式,小例子供大家使用啊.CSS鼠标样式 ...