1. 首先,我们快速过一下,什么是Block?

Block是一段代码,它在OC中以^开头,可以有返回值,和参数列表,但就是没有名字。

所以,你可以把它认为是匿名函数。

事实上,它和Swift中的闭包(Closure)是一样的。

或者,学过.NET的童鞋知道委托吧,它和委托也差不多概念。

都是可以在一个方法中传入它,作为参数的方法。

无参无返回值的Block:

[MyObject myMethodParam1: xx param2: ^{

  ...

}];

有参有返回值的Block:

[MyObject myMethodParam1: xx param2: ^BOOL(id param1, id param2) {

  ...

}];

好了,说了那么多,我们来看个例子:

myDict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
NSLog(@"Key - %@: Value - %@", key, value);
if ([@"END" isEqualToString:key]) {
*stop = YES;
}
}];

上面这段代码枚举一个字典的键值对,知道遇到END键值退出循环,否则枚举所有的键值对。

2. 在Block中我们可不可以使用Block范围以外声明的变量呢?

答案是可以的,但是它是只读的,你如果要修改这个变量,会编译错误。

我们还是来看上面那段代码的例子,在循环中,我们增加了一个外部变量,想要让Block提早结束

BOOL stopEarly = NO;
double stopValue = 100.2;
[myDict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
NSLog(@"Key - %@: Value - %@", key, value);
if ([@"END" isEqualToString:key] || [value doubleValue] == stopValue) {
*stop = YES;
stopEarly = YES; // 这段代码编译会出错!!
}
}];

上面的代码stopValue变量的Block内读取没有任何问题,但是当我们企图在Block内修改stopEarly变量的值时,编译出错了!

那么如果,我们执意要修改Block外面的变量,是不是可以呢?

答案是可以的。

我们要使用__block关键字,原理上是通过使用该关键字,我们可以把block外的变量从栈中移到堆中,这样就可以在Block内部使用了。

当Block结束时,变量又回到栈中。

还是上面的代码,我们作下修改,如下:

__block BOOL stopEarly = NO;
double stopValue = 100.2;
[myDict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
NSLog(@"Key - %@: Value - %@", key, value);
if ([@"END" isEqualToString:key] || [value doubleValue] == stopValue) {
*stop = YES;
stopEarly = YES;
}
}]; if (stopEarly)
NSLog(@"Block提前终止了!");

3. 当我们向Block内的对象发送消息时,系统会创建一个指向该对象的强指针

该强指针会一直保留到Block超出自己的范围,不存在。

4. Block数组

property (nonatomic, strong) NSMutableArray *blockArray;

...
- (void) someMethod {
[self.blockArray addObject:^{
...
}];
} // 获取数组中的block并调用
void (^doit)(void) = self.blockArray[];
doit();

5. 常见的Block中导致循环引用(Memory Cycle)的场景

前面我们曾经提到:

当我们向Block内的对象发送消息时,系统会创建一个指向该对象的强指针

该强指针会一直保留到Block超出自己的范围。

换句话说,Block内的所有对象,都会在堆上保留自己的内存,也就是说,Block会保留

指向这些对象的强指针(strong pointer)。

我们来看下面的代码:

@property (nonatomic, strong) NSMutableArray *blockArray;

...

// in the blockArray a strong pointer has been kept to the self object
// while self holds another strong pointer to blockArray
- (void) someMethod {
[self.blockArray addObject:^{
[self doSomething];
}];
}

上面的代码的问题是:blockArray保留了一个指向类对象的强引用指针,而类对象,也就是self也保留了

一个指向blockArray的强引用指针。

大家都有强指针指向对方, 它们都无法从栈中释放,这就导致了内存的循环引用。

解决方案:
为了打破循环引用,我们要使用一个弱类型的局部变量。
什么意思?
由于局部变量都是强指针类型,都保留指向堆中的强指针,所以我们要想办法创建一个弱指针类型的局部变量来
打破循环引用。
可以使用关键字:
__weak
比如:
__weak MyClass *weakSelf = self;
这样即使self本身是强指针类型的,而我们创建的weakSelf却变成了弱指针类型。

于是,上面有问题的代码我们可以进行如下的修改:

@property (nonatomic, strong) NSMutableArray *blockArray;

...

- (void) someMethod {
__weak MyClass *weakSelf = self; [self.blockArray addObject:^{
[weakSelf doSomething];
}];
}

这样block这边保留的对于对象的指针就变成了弱类型的指针,循环引用被打破了!
这是一个常用的解决方案,必须要记住!!

Objective-C编程 - 关于Block的要点的更多相关文章

  1. OC高级编程——深入block,如何捕获变量,如何存储在堆上

    OC高级编程——深入block,如何捕获变量,如何存储在堆上   首先先看几道block相关的题目 这是一篇比较长的  博文 ,前部分是block的测试题目,中间是block的语法.特性,block讲 ...

  2. iOS开发技巧系列---使用链式编程和Block来实现UIAlertView

    UIAlertView是iOS开发过程中最常用的控件之一,是提醒用户做出选择最主要的工具.在iOS8及后来的系统中,苹果更推荐使用UIAlertController来代替UIAlertView.所以本 ...

  3. ios block常见的错误(三)——并发编程的block引用

    在一些技术型的企业里面,有关block面试笔试题,将会问得很深,如下例子: 请问DemoObj的对象能否正确释放,为什么? //DemoObj.m @interface DemoObj() @prop ...

  4. Objective-C( block的使用)

    block block用来保存一段代码 block的标志:^ block跟函数很像:可以保存代码.有返回值.有形参.调用方式跟调用方法一样 block内部可以访问外面的变量 默认情况下,block内部 ...

  5. 【转载】学习C++和编程的几个要点

    1.把C++当成一门新的语言学习(和C没啥关系!真的.):2.看<ThinkingIn C++>,不要看<C++变成死相>:3.看<The C++ Programming ...

  6. Block使用要点

    Block简介 Block其实包含两个部分内容 Block执行的代码,这是在编译的时候已经生成好的: 一个包含Block执行时需要的所有外部变量值的数据结构. Block将使用到的.作用域附近到的变量 ...

  7. Python并行编程的几个要点

    一.基于线程的并行编程 如何使用Python的线程模块 如何定义一个线程 如何探测一个线程 如何在一个子类中使用线程 Lock和RLock实现线程同步 信号实现线程同步 条件(condition)实现 ...

  8. Delphi编程中动态菜单要点归纳

      一.创建菜单并添加项目 在设计程序时,有时需要动态创建菜单, 通常使用以下的语句: PopupMenu1 := TPopupMenu.Create(Self);  Item := TMenuIte ...

  9. PHP编程20大效率要点

    1.如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍. 2.$row[’id’] 的速度是$row[id]的7倍. 3.echo 比 print 快,并且使用ech ...

随机推荐

  1. 使用Axure RP原型设计实践03,制作一个登录界面的原型

    本篇体验做一个登录界面的原型. 登录页 首先在Page Style里为页面设置背景色. 如果想在页面中加图片,就把Image部件拖入页面,并设置x和y轴.双击页面中的Image部件可以导入图片.在Im ...

  2. JQuery攻略(四)事件

    jQuery事件处理,鼠标的单击,双击,悬停,键盘按键,文本动画..... 此章节有 1.1被点击的按钮查找 1.2事件的自动触发 1.3点击之后禁用按钮 1.4鼠标事件 1.5焦点事件 1.6CSS ...

  3. Android之在string.xml配置文字颜色粗体等效果

    string.xml <string name="exchange_txt_hint"><Data><![CDATA[请使用<font colo ...

  4. 安卓之上传文件,即HTTP提交表单

    获取文件: public void Init()    {        noScrollgridview = (GridView) findViewById(R.id.noScrollgridvie ...

  5. ImportError: No module named model_libs

    在运行ssd时遇到这个问题 实际是python接口的路径不对,使用echo $$PYTHONPATH  弹出当前python路径,发现是caffe自己的python接口,采用 export PYTHO ...

  6. Reloading Java Classes 301: Classloaders in Web Development — Tomcat, GlassFish, OSGi, Tapestry 5 and so on Translation

    The Original link : http://zeroturnaround.com/rebellabs/rjc301/ Copyright reserved by Rebel Inc In t ...

  7. Swift3.0:NSURLConnection的使用

    一.介绍 应用中也不必不可少的会使用网络通信,增强客户端和服务器的交互,可以使用NSURLConnection实现http通信. NSURLConnection提供了异步请求和同步请求两种请求方式.同 ...

  8. 第二章 logstash - 输出插件之redis与es

    最常用的两个输出插件: redis es 一.redis 1.用法 output { redis{ batch => false batch_events => 50 batch_time ...

  9. HTML JS 数据校验

    用到了html字符串校验,这里记录一下. <html> <head> <script type="text/javascript"> funct ...

  10. C++ 内置宏定义 与 预编译指令

    内置宏和预编译指令, 在代码调试.单元测试.跨平台代码中经常会用到.这里记录一下. 1. 内置宏 (文件名,当前行号,当前日期,当前时间,当前执行方法名) __FILE____LINE____DATE ...