Objective-C编程 - 关于Block的要点
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的要点的更多相关文章
- OC高级编程——深入block,如何捕获变量,如何存储在堆上
OC高级编程——深入block,如何捕获变量,如何存储在堆上 首先先看几道block相关的题目 这是一篇比较长的 博文 ,前部分是block的测试题目,中间是block的语法.特性,block讲 ...
- iOS开发技巧系列---使用链式编程和Block来实现UIAlertView
UIAlertView是iOS开发过程中最常用的控件之一,是提醒用户做出选择最主要的工具.在iOS8及后来的系统中,苹果更推荐使用UIAlertController来代替UIAlertView.所以本 ...
- ios block常见的错误(三)——并发编程的block引用
在一些技术型的企业里面,有关block面试笔试题,将会问得很深,如下例子: 请问DemoObj的对象能否正确释放,为什么? //DemoObj.m @interface DemoObj() @prop ...
- Objective-C( block的使用)
block block用来保存一段代码 block的标志:^ block跟函数很像:可以保存代码.有返回值.有形参.调用方式跟调用方法一样 block内部可以访问外面的变量 默认情况下,block内部 ...
- 【转载】学习C++和编程的几个要点
1.把C++当成一门新的语言学习(和C没啥关系!真的.):2.看<ThinkingIn C++>,不要看<C++变成死相>:3.看<The C++ Programming ...
- Block使用要点
Block简介 Block其实包含两个部分内容 Block执行的代码,这是在编译的时候已经生成好的: 一个包含Block执行时需要的所有外部变量值的数据结构. Block将使用到的.作用域附近到的变量 ...
- Python并行编程的几个要点
一.基于线程的并行编程 如何使用Python的线程模块 如何定义一个线程 如何探测一个线程 如何在一个子类中使用线程 Lock和RLock实现线程同步 信号实现线程同步 条件(condition)实现 ...
- Delphi编程中动态菜单要点归纳
一.创建菜单并添加项目 在设计程序时,有时需要动态创建菜单, 通常使用以下的语句: PopupMenu1 := TPopupMenu.Create(Self); Item := TMenuIte ...
- PHP编程20大效率要点
1.如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍. 2.$row[’id’] 的速度是$row[id]的7倍. 3.echo 比 print 快,并且使用ech ...
随机推荐
- springboot之异步调用@Async
原文:http://www.cnblogs.com/xuwenjin/p/8858050.html 引言: 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的:但是在处理与第三方系统交 ...
- INDY10的IDHttpServer应答客户端
INDY10的IDHttpServer应答客户端 首先贴源码: procedure TIdHTTPResponseInfo.WriteContent; begin if not HeaderHasBe ...
- AIDL interface XXX should be declared in a file
在写AIDL的时候出现了interface XXX should be declared in a file, 错误...经过反复查看,发现AIDL规定,文件名必须和interface XXX名字相同 ...
- .Net中集合排序
public class StockQuantity { public StockQuantity(string status, DateTime dateTime, int quantity) { ...
- spring mvc改动配置文件路径
1.1. Classpath project文件夹 在web.xml文件例如以下配置: <!-- 配置spring mvc 的核心servlet --> <servlet> ...
- 详解Java Spring各种依赖注入注解的区别
注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解有Autowired.Resource.Qualifier.Service.Controller.Repository.Comp ...
- 用make编译openCV报错:ts_gtest.cpp:(.text._ZN7testing8internal2RED2Ev+0xf): undefined reference to 'regfreeA'
解决方案: the cause is the google tests is looking for the generic regex.h but cmake used the regex.h fr ...
- 磁共振中的T1, T2 和 T2*的原理和区别
从物理的角度,要理解这几个概念的区别,需要对原子核的磁化有所了解,本文通过一些图示对这几个概念进行简明的介绍. 首先,磁共振最基本的原理就是氢原子核在磁场中自旋运动时所具有的量子力学特性.在一个均匀磁 ...
- 机器翻译评价指标之BLEU详细计算过程
原文连接 https://blog.csdn.net/guolindonggld/article/details/56966200 1. 简介 BLEU(Bilingual Evaluation Un ...
- Guava BiMap AbstractBiMap
BiMap BiMap是一个结构,他定义了一个Map结构,代表这个Map的key和value都具有唯一性, 并且可以生成相互联系的反向视图, 反向视图的数据会随着本体BiMap的变更而变更 /* * ...