Chapter 6. Blocks and Grand Central Dispatch
<br />
Item 37: Understand Blocks
<br />
这一节讲block的一些比较基本的内容。
block也是一个OC对象,所以也会有引用计数以及保留环的问题。特别是在块内部引用或修改iVar的时候要特别小心,因为此时block也指向了iVar归属的那个对象,如果这个对象(比如常见的self)也指向了block,就会造成保留环。
block的存储结构:
像其他OC对象一样,isa指针指向类。invoke是一个函数指针,函数的第一个参数代表的就是block,类型是void *,函数指针指向block的实现代码。descriptor指针指向一个结构体,里面是描述block特征的一些参数。block里引用的变量都会被copy到“Captured Varaibles”这部分里,这里用的是浅拷贝,就是只是给变量多加了一个由block指向的指针。
block默认是放在栈里,就类似全局变量,在大括号结束以后就可能被释放掉。如果想避免block被释放引起的bug,就要用copy把block复制到堆上。文中的例子:
void (^block)();
if (/* some condition */) {
block = [^{
NSLog(@“Block A”);
} copy];
} else {
block = [^{
NSLog(@“Block B”);
} copy];
}
block();
最后提到的是global block。没太见过,搜了一下没搜到太多结果,不知道是不是常用。是一类不需要引用外界的变量,内部实现在编译期可以完全确定的block。因为这种特点,所以可以直接优化分配到静态内存的部分,就像global/static变量,也类似单例。这样的block就可以直接使用,复制的话就增加指针个数,不用再创建新的block到栈中。
<br />
Item 38: Create typedefs for Common Block Types
<br />
这一篇讲用typedef給block起个名字的重要性。
看到这我又想起最早的时候看《Programming with Objective-C》的时候遇到的例子了。例子长这样子:
void (^(^complexBlock)(void(^)(void)))(void) = ^(void(^aBlock)(void)) {
…
return ^{
…
};
};
化简完是这样:
typedef void (^XYZSimpleBlock)(void);
XYZSimpleBlock (^betterBlock)(XYZSimpleBlock) = ^ (XYZSimpleBlock aBlock) {
…
return ^{
…
};
}
当时直接就蒙圈了。其实现在看还是需要绕一会。感谢当时大神帮我找了个解释,在这里:http://www.cocoawithlove.com/2009/10/ugly-side-of-blocks-explicit.html 简单来说,block的写法和函数指针的写法是类似的,用读函数指针的顺序来读block就可以看懂而且看得更清晰。上面的例子里最难看懂的一步是^(^complexBlock)(void(^)(void))
表示的是complexBlock的返回值是一个block类型。实际中恐怕也不会遇到这样的例子,只是因为印象太深了所以提一下。
用typedef给block起名字的好处有:
- 首先肯定是易读多了。
- 然后是需要修改的时候,可以先修改typedef的地方,然后根据报错一个一个去修改,不会遗漏。
另外命名方面,如果block所做的事情是某个类特有的,就应该把类名写在前面。即便block的形式相同(指参数个数与类型、返回值类型),在不同的类里起不同作用时也应该分别去定义。如果是兄弟关系的类使用相同的block,则应定义在父类里。
<br />
Item 39: Use Handler Blocks to Reduce Code Separation
<br />
这一节讲block作为参数的好处,handler block是比较常见的一类情况。
block作为参数直接展开写在方法调用里,好处是写起来方便,还使代码变得紧凑,block的内容和调用它的对象都写在一起。这也是设计block的初衷之一,对比代理和函数指针,这一点的优势尤其明显。
Handler blocks在文中主要指网络请求完毕的回调block。一般有两种设计,一种是把成功和失败作为两个参数,各写一个block进行处理。另一种是只写一个的completion block,通过传入error变量来判断不同的情况然后分别处理。
比较推荐的是后一种设计,和官方的风格比较相似,而且处理更加灵活,不是简单地二分为”成功“或”失败“,可以处理更多的情况,比如返回的数据不符合要求等。
另外一个用法是,用在某些必须指定在某个线程运行的方法上。举例是通知中心的一个方法:
-(id)addObserverForName:(NSString *)name object:(id)object queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *))block
这里第三个参数就是确定要在哪个队列运行,最后一个参数表示要在这个队列运行的代码块。
网友评论