1. block基本概念
Block是C级别的语法和运行时特性。Block是一种比较特殊的数据类型,它可以保存一段代码,在合适的时候取出来调用。Block比较类似C函数,但是Block比之C函数,其灵活性体现在栈内存、堆内存的引用。Block是苹果推荐的类型,效率高,可以帮助我们组织独立的代码段,并提高复用性和可读性。主要是用来在运行中封装代码和保存代码用的,Block可以在任何时候被执行。
从 xcode 4.0 开始,系统类库中的函数越来越多的开始使用 block 作为参数,以下是在系统函数中使用block的部分情况:a.遍历数组和字典b.排序c.视图动画d.结束回调e.错误处理f.多线程等
格式说明:(返回类型)(^块名称)(参数类型列表) = ^(形参列表) {代码实现};
如果没有参数,等号后面参数列表的()可以省略
2. 需要注意的地方
a. block(代码块)是 oc中的一种数据类型,可以被当做参数传递,可以有返回值,是一个能工作的代码单元,可以在任何需要的时候被执行,就像调用函数一样调用,在 ios 开发中广泛使用。
b. ^ 是 block 的特有的标记。
c. block 熟练了解block 的定义(块代码的定义),记得实现代码包含在 {} 之间。
d. block 是以内联 inline 函数的方式被定义使用。
e. 本质上是轻量级的匿名函数。
c. 块代码的使用注意点
i. 默认情况下,不允许在块代码内部修改外部的变量的数值
ii. __block,让外部的变量能够在block中修改
iii. 循环引用的问题 __weak (ios5.0以下的版本使用__unsafe_unretained)
iv. 默认情况下,block 外部的变量,在 block 中是只读的
v. 块代码与代理的区别(代理和block是IOS上实现回调的两种机制。Block基本可以代替代理的功能,而且实现起来比较简洁,比较推荐能用block的地方不要用代理;单就编程过程而言,block对开发者处理逻辑,编程效率,代码阅读都有积极影响。代理是一种很经典的模式,我们很多人都已经习惯了这种模式,若果对block的回调传值的过程不是很理解的话,建议使用代理,可以达到同样地效果。)
3.block的修饰
ARC情况下
1.如果用copy修饰Block,该Block就会存储在堆空间。则会对Block的内部对象进行强引用,导致循环引用。内存无法释放。
解决方法:新建一个指针(__weak typeof(Target) weakTarget = Target )指向Block代码块里的对象,然后用weakTarget进行操作。就可以解决循环引用问题。
2.如果用weak修饰Block,该Block就会存放在栈空间,不会出现循环引用问题。
MRC情况下
用copy修饰后,如果要在Block内部使用对象,则需要进行(__block typeof(Target) blockTarget = Target )处理。在Block里面用blockTarget进行操作。
4.关于block引用外部变量操作的问题
block 使用,如果引用了外部变量,就会对外部变量做一个copy的操作,记录住定义block时候的值。如果后续再修改外部变量的值,不会影响block内部的数值的变化。
外部变量本来是在栈区中,block引用的那一刻,就将外部变量copy 到堆区中了,block里面使用的时copy 后堆中的变量的值。
所有在block 调用之前修改外部变量的值,不会影响block里面值的原因。
(如果要验证:可以通过打印地址值的方式来验证,栈区是高位地址值,相对于栈,堆在低位地址。通过打印地址发现,block里面的变量的地址值比block外面的地址值要小很多。)
在默认的情况下,是不允许在block内部修改外部变量的值。(原因是:会破坏代码的可读性,不易于维护)
如果我们一定要在block的内部修改外部变量的值,必须在外部变量的前面添加 _ _block ,这样才会允许修改。使用__block,说明不再关心外部变量数值的具体变化。
为什么使用__block 会达到这个效果(可以通过跟踪地址值发现问题)?
在定义block时,如果引用了外部变量使用了__block的变量。block定义之后,外部变量同样会被copy到堆中,不同的是栈中的那一份没有了,只保留了堆中的那一份。在block 中修改的那一份和保留的那一份是同一份,所以可以修改。
5.block的类型
block有几种不同的类型,这里列出常见的三种类型:
_NSConcreteGlobalBlock:全局的静态block,不会访问任何外部变量,不会涉及到任何拷贝,比如一个空的block。它既不在栈中,也不在堆中,我理解为它可能在内存的全局区。
_NSConcreteStackBlock:保存在栈中的block,当函数返回时被销毁。_NSConcreteStackBlock类型的block有闭包行为,也就是有访问外部变量,并且该block只且只有有一次执行,因为栈中的空间是可重复使用的,所以当栈中的block执行一次之后就被清除出栈了,所以无法多次使用。
_NSConcreteMallocBlock:保存在堆中的block,当引用计数为0时被销毁。该类型的block都是由_NSConcreteStackBlock类型的block从栈中复制到堆中形成的。