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. 在Visual Studio中使用组件图描述项目组件依赖关系

    如果想描述项目组件的关系,可以考虑使用UML组建图. 在建模项目下添加一个名称为"Applicaiton Component Structure"的UML组建图. 添加各个组件,并 ...

  2. 迭代dict的value

    我们已经了解了dict对象本身就是可迭代对象,用 for 循环直接迭代 dict,可以每次拿到dict的一个key. 如果我们希望迭代 dict 对象的value,应该怎么做? dict 对象有一个 ...

  3. mysql慢查询监控及sql优化

    在my.ini添加如下代码,即可查看那个sql语句执行慢了 log-slow-queries = d:/log/mysql-slow.log long_query_time = 1 打开日志 log ...

  4. [rrdtool]监控和自己主动绘图,简单的监控.md

    如今想要监控服务的流量和并发数,但是又没那么多时间来写系统.其它的运维系统又不熟悉,于是就用现有的rrdtool shell做了个简单的监控界面,暂时用下,也算是个小实验把. rrdtool也是刚接触 ...

  5. 监听Listview的滚动状态,是否滚动到了顶部或底部

    /** * @author:Jack Tony * @description : 监听listview的滑动状态,如果到了顶部就刷新数据 * @date :2015年2月9日 */ private c ...

  6. TableLayout中collapseColumns,stretchColumns的介绍

     设置后→  collapseColumns  设置需要被隐藏的列序号(序号从0开始) shrinkColumns     设置允许被首夺的列的序号(序号从0开始) stretchColumns   ...

  7. [Web 前端] 如何在React中做Ajax 请求?

    cp from : https://segmentfault.com/a/1190000007564792 如何在React中做Ajax 请求? 首先:React本身没有独有的获取数据的方式.实际上, ...

  8. [转]使用mysql profiles 来查看sql 语句执行计划

    From : http://blog.csdn.net/radkitty/article/details/4632289 要使用该功能,mysql的版本必须在5.0.37版本以上.否则只能使用expl ...

  9. dubbo源码解析-spi(3)

    前言 在上一篇的末尾,我们提到了dubbo的spi中增加了IoC和AOP的功能.那么本篇就讲一下这个增加的IoC,spi部分预计会有四篇,因为这东西实在是太重要了.温故而知新,我们先来回顾一下,我们之 ...

  10. Log Shipping搭建

    1.    概述 SQL Server 使用日志传送,您可以自动将“主服务器”实例上“主数据库”内的事务日志备份发送到单独“辅助服务器”实例上的一个或多个“辅助数据库”.事务日志备份分别应用于每个辅助 ...