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 ...
 
随机推荐
- android编译错误“OnClickListener cannot be resolved to a type”
			
在android代码编译时可能会出现如下错误: 部分代码: <span style="font-size:18px;">public void onCreate(Bun ...
 - Odoo(OpenERP)应用实践:代发货管理
			
原文地址:http://blog.csdn.net/wangnan537/article/details/47091857 有些分销商,在买家下单后才向供应商采购产品,并由供应商直接发货给客户.这种模 ...
 - sqlite 截取字符串函数substr
			
函数:substr(string string,num start,num length) 用法: string为字符串: start为起始位置:字符串的第一个字符的位置为1,不是从0开始计算 len ...
 - SpringMVC从Controller跳转到还有一个Controller
			
1. 需求背景 需求:spring MVC框架controller间跳转,需重定向.有几种情况:不带參数跳转.带參数拼接url形式跳转,带參数不拼接參数跳转,页面也能显示. 本来以为挺简单的一件事情. ...
 - Swift - 添加纯净的Alamofire
			
Swift - 添加纯净的Alamofire 如果你有代码洁癖,不能容忍任何多余的东西,请继续往下看. . 下载Alamofire (https://github.com/Alamofire/Ala ...
 - 维京传奇第四季/全集Vikings迅雷下载
			
英文全名Vikings,第4季(2015). 本季看点:<维京传奇>当第四季开始时,Ragnar已经病入膏肓,随时可能丧命.毫无疑问,权力斗争异常激烈,谁都想在Ragnar死后获得最大利益 ...
 - SpiderMonkey js引擎的静态编译与使用,  SpiderMonkey的使用
			
SpiderMonkey js引擎的静态编译与使用 2017年10月02日 02:51:22 yaolixing01 阅读数:536 原文出处: http://yaolixing.oltag.co ...
 - kafka-python的gevent模式和kafka的兼容性
			
使用gevent会杀死kafka的consumer线程:据查:kafka-python对gevent的支持不是太好,可以使用pykafka:但是可以kafka-python可以结合eventlet使用 ...
 - Netty Associated -- ByteBuf
			
ByteBuf ByteBuf是Netty的Server与Client之间通信的数据传输载体.他提供了一个byte数组(byte[])的抽象视图 buffer创建 我们推荐通过一个Unpooled的帮 ...
 - MVC详解(转)
			
原文链接:MVC详解 MVC与模板概念的理解 MVC(Model View Controller)模型-视图-控制器 MVC本来是存在于Deskt op程序中的,M是指数据模型,V是指用户界面,C ...