Variable Attributes 其实是 GCC 中用于描述变量的一种修饰符。我们可以使用 __attribute__ 来修饰一些变量来参与静态分析等编译过程;而在 Cocoa Touch 中很多的宏其实都是通过 __attribute__ 来实现的,例如:
#define NS_ROOT_CLASS __attribute__((objc_root_class))
而 cleanup 就是在这里会使用的变量属性:
The cleanup attribute runs a function when the variable goes out of scope. This attribute can only be applied to auto function scope variables; it may not be applied to parameters or variables with static storage duration. The function must take one parameter, a pointer to a type compatible with the variable. The return value of the function (if any) is ignored.
GCC 文档中对 cleanup 属性的介绍告诉我们,在 cleanup 中必须传入 只有一个参数的函数并且这个参数需要与变量的类型兼容。
如果上面这句比较绕口的话很难理解,可以通过一个简单的例子理解其使用方法:
void cleanup_block(int *a) {
printf(“%d\n”, *a);
}
int variable __attribute__((cleanup(cleanup_block))) = 2;
在 variable 这个变量离开作用域之后,就会自动将这个变量的指针传入 cleanup_block 中,调用 cleanup_block 方法来进行『清理』工作。
实现 defer
到目前为止已经有了实现 defer 需要的全部知识,我们可以开始分析 libextobjc 是怎么做的。
在 libextobjc 中并没有使用 defer 这个名字,而是使用了 onExit(表示代码是在退出作用域时执行)
为了使 onExit 在使用时更加明显,libextobjc 通过一些其它的手段使得我们在每次使用 onExit 时都需要添加一个 @ 符号。
{
@onExit {
NSLog(“Log when out of scope.”);
};
NSLog(“Log before out of scope.”);
}
onExit 其实只是一个精心设计的宏:
#define onExit \
ext_keywordify \
__strong ext_cleanupBlock_t metamacro_concat(ext_exitBlock_, __LINE__) __attribute__((cleanup(ext_executeCleanupBlock), unused)) = ^
既然它只是一个宏,那么上面的代码其实是可以展开的:
autoreleasepool {}
__strong ext_cleanupBlock_t ext_exitBlock_19 __attribute__((cleanup(ext_executeCleanupBlock), unused)) = ^ {
NSLog(“Log when out of scope.”);
};
这里,我们分几个部分来分析上面的代码片段是如何实现 defer 的功能的:
1. ext_keywordify 也是一个宏定义,它通过添加在宏之前添加 autoreleasepool {} 强迫 onExit 前必须加上 @ 符号。
#define ext_keywordify autoreleasepool {}
2. ext_cleanupBlock_t 是一个类型:
typedef void (^ext_cleanupBlock_t)();
3. metamacro_concat(ext_exitBlock_, __LINE__) 会将 ext_exitBlock 和当前行号拼接成一个临时的的变量名,例如:ext_exitBlock_19。
4. __attribute__((cleanup(ext_executeCleanupBlock), unused)) 将 cleanup 函数设置为 ext_executeCleanupBlock;并将当前变量 ext_exitBlock_19 标记为 unused 来抑制 Unused variable 警告。
5. 变量 ext_exitBlock_19 的值为 ^{ NSLog(“Log when out of scope.”); },是一个类型为 ext_cleanupBlock_t 的 block。
6. 在这个变量离开作用域时,会把上面的 block 的指针传入 cleanup 函数,也就是 ext_executeCleanupBlock:
void ext_executeCleanupBlock (__strong ext_cleanupBlock_t *block) {
(*block)();
}
这个函数的作用只是简单的执行传入的 block,它满足了 GCC 文档中对 cleanup 函数的几个要求:
只能包含一个参数
参数的类型是一个指向变量类型的指针
函数的返回值是 void
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !