总结
一、理解“块”这一概念
1、块的基础知识
(1)、概念:
块用“^”符号来表示,后面跟着一对花括号,括号里面是块的实现代码。例如:
^{
// Block implementation here
}
块其实就是个值,也有其相关类型,可以把块赋给变量并使用它。块类型的语法与函数指针近似。例如,无参数无返回值的块类型定义如下:
void (^someBlock)() = ^{
// Block implementation here
};
因此,我们可以把块类型的语法结构写成这样子,如下:
return_type (^block_name) (parameters)
例子:
int (^addBlock) (int a, int b) = ^(int a, int b) {
return a + b;
};
(2)、块的内部结构
块本身也是对象,在存放块对象的内存区域中,首个变量是指向Class对象的指针(isa),下图描述了块对象的内存布局。
在内存布局中,最重要的就是invoke变量,这是个函数指针,指向块的实现代码。
(3)、全局块、栈块及堆块
定义块的时候,其所占的内存区域是分配在栈中的。
若要在块定义的范围之外使用,则需给块对象发送copy消息以拷贝之,这样就把块从栈复制到堆了,拷贝后的块就可以在定义它的那个范围之外使用了。
块也可以定义成全局的。
2、为常用的块类型创建typedef
为了隐藏复杂的块类型,需要使用类型定义,如:
typedef int (^EOCSomeBlock) (BOOL flag, int value);
EOCSomeBlock block = ^(BOOL flag, int value) {
//Implementation
};
3、用 handler 块降低代码分散程度
在程序里使用delegate的地方都可以使用Block来替换,其使用方法如下:
typedef void (^EOCNetworkFetcherCompletionHandler) (NSData *data, NSError *error);
@interface EOCNetworkFetcher : NSObject
- (void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)handler;
@end
4、用块引用其所属对象时不要出现保留环
1、如果块所捕获的对象直接或间接地保留了块本身,那么久得当心保留环的问题。
2、一定要找个适当的时机解除保留环。
5、多用派发队列,少用同步锁
1、有种简单而高效的办法可以代替同步块或锁对象,那就是使用“串行同步队列”。
dispatch_queue_t _syncQueue = dispatch_queue_create("com.effectiveobjectivec.syncQueue", NULL);
- (NSString *)someString {
__block NSString *localSomeString;
dispatch_sync(_syncQueue, ^{
localSomeString = _someString;
});
return localSomeString;
}
- (void)setSomeString:(NSString *)someString {
dispatch_sync(_syncQueue, ^{
_someString = someString;
});
}
进一步优化,可以将设置实例变量的方法中所用到的块同步改成异步:dispatch_async(_syncQueue, ^{});