我们知道,不同对象之间通信的方法比较常用的有代理(delegate)、通知中心(NotificationCenter)等方法。Block则是另一种对象间通信的方法。其中,delegate和block相关的两个对象是一对一的关系,通知中心所反映的则是一对多的关系,通过这些方法实现了对象间的解耦合(功能相关,但没有继承与派生关系)。
1、简介
Block在ios 4.0之后加入,并大量使用在新的ios api中。block是一个匿名的代码块,可以作为传递给其他对象的参数,并得到返回值。从本质上讲,block同其他普通的变量类似,只是其储存的数据是一个函数体。Block不只是针对Objective-C的专利,而是一种可以应用于C、C++和OBjective-C的语言层面的新特性。通过使用block,开发者可以将一段代码段像某一个数值一样当做参数传递给函数。同时,blocks也是Objective-C的一种对象,可以像其他对象一样添加到NSArray或者NSDictionary等集合中。
2、使用方法
(1)声明一个block:
//返回值 Block变量 参数 int (^myBlocks)(int);
(2)创建一个block:
myBlocks = ^(int a) { int result = a * a; return result; }
(3)调用block:
int ret = myBlocks(10);
熟悉C/C++的TX也许有一种感觉,即ios的block机制十分类似前者的函数指针这一概念。
(4)声明block的类型:利用typedef简化block的定义和实现,尤其是对同一种格式定义多个block时更加明显。
typedef int (^myBlocks) (int); MyBlocks myBlock = ^(int a) { int result = a*a; return result; }
3、block作为函数的参数:
通过将block作为函数的参数进行传递,可以实现类似回调函数的功能;
.....
4、Block中的变量
Block中可以使用全局变量和局部变量,对二者的处理有所不同。block引用局部变量时,将该变量作为常量编码到代码块中;如果想在block中进行修改,则必须使用__block进行修饰。如:
//==========错误写法========== int number = 10; myBlock = ^(int a) { number = 20;//number被处理成常量,不能修改。 NSLog("%@",number); } //==========正确写法========== __block int number = 10; myBlock = ^(int a) { number = 20;//__block修饰的局部变量可以在block内部修改 NSLog("%@",number); }
5、Block的内存管理
对于在block内部引用的对象,__block也是关键因素。如果在block内部引用了一个局部对象,那么该对象的引用计数会+1;除非该对象由__block修饰,那么其引用计数不变。这么做的主要原因是block的代码经常涉及到延迟执行的情况,所以ios将block内部涉及到的对象进行retain,防止在block中的代码执行之前该对象就被释放掉。
对block的内存管理通常可以使用copy和release方法。在创建之初,block的内存分配在栈中,copy之后转移到堆中。
避免在实现block时发生对self的强循环引用:
简而言之,如果一个类包含一个block作为property,那么这个block的copy属性可以认为是self对block的强引用,而一个block对其内部调用的变量和对象同样存在强引用关系。那么如果一个block在实现时调用了self的方法,那么将会造成self和block的强循环引用,使得内存无法被释放。为了避免这种情况,在block内部调用self的时候,需要重新定义一个由__weak修饰的self副本,并以该副本调用self的方法,如下代码所示:
- (void)configureBlock { XYZBlockKeeper * __weak weakSelf = self; self.block = ^{ [weakSelf doSomething]; // capture the weak reference } }
除了上述的block的主要作用之外,还可以使用block简化集合类的枚举操作,以及简化并行任务等,详见官方文档。