宏定义不仅仅局限于一些简单的常用固定标记,对于可复用的的可移植的小功能性代码,也可以采用宏定义封装,提高开发效率
本文通过一个实际例子引入主题
场景
在写网络请求的代码时,通常会做这样一个处理,异步操作回调。这段代码是通用的,所以可以利用宏定义抽取出来,做可复用的封装,下面通过代码解读。
代码
#define within_main_thread(block,...)\
do {\
if ([[NSThread currentThread] isMainThread]) {\
if (block) {\
block(__VA_ARGS__);\
}\
} else {\
if (block) {\
dispatch_async(dispatch_get_main_queue(), ^{\
block(__VA_ARGS__);\
});\
}\
}\
} while (0);
分析
以上的代码很简单,大致的逻辑显而易见,这里针对几个地方进行详细分析:
-
within_mian_thread(block,...)
第一个参数是block
,第二个为可变参数,在宏定义的实际代码中可以发现为__VA_ARGS__
,并且被用作了block
的参数,这样就增加了宏的可拓展性。 -
do{} while(0)
效果和不用它是一样的,程序只执行一次,使用它的目的是:将实际代码包装在了括号中,这样在编译时替换为实际代码时就更加稳定。由这个目的我们还可以想到其他手段,例如@try{} @finally{}
,将代码放在@finally
的括号中。
实际使用
- (void)getData :(void (^)(NSString *, NSNumber *))complete {
// 从网络获取到两个数据,str和num
if (complete) {
within_main_thread(complete, str, num);
}
}
这里可能会有一个疑惑,从网络获取到的数据为何要在主线程中设置,在异步线程中设置不也一样吗。这里的原因是考虑到安全性和封装质量,在主线程设置数据可以让其他任何地方使用时不用关心数据的安全问题。