探索 Availability Checking 内部实现
这篇文章我们一步一步探索@avaliable的本质。
WWDC 2017: What’s New in LLVM 中苹果介绍了一种新的API可用性检查方法,使用@avaliable等类似的语法。详细可见这篇文档 Marking API Availability in Objective-C
其中 @available()可用于判断语句中,如下:
if (@available(iOS 11, *)) {
// Use iOS 11 APIs.
} else {
// Alternative code for earlier versions of iOS.
}
相信这也是大家最熟悉的,那么@avaliable到底是什么呢?
最简单的例子
我们新建一个iOS工程,然后AppDelegate里调用下如下代码:
void test_available() {
if (@available(iOS 11, *)) {
// Use iOS 11 APIs.
NSLog(@"ios11");
} else {
// Alternative code for earlier versions of iOS.
NSLog(@"ios other");
}
}
例子代码见这里
逆向看看
编译(模拟器即可)后,使用Hopper打开生成的可执行文件,找到这个 test_available,如图:

看下反编译的伪代码:

可知调用了 ___isOSVersionAtLeast 这个函数,0xb就是iOS 11,估计后面两个0x0就是第二位和第三位版本了。
然后Hopper找到这个函数,

一眼看去,猜测大概就是dispatch_once里获取了当前系统的版本,然后赋值给_GlobalMajor、_GlobalMinor和_GlobalSubminor三个全局变量。
那么dispatch_once里是怎么获取的系统版本呢?Hopper的反汇编出的伪代码似乎看不出block中执行了什么了。切换到汇编代码视图,可见:

这里调用了 _parseSystemVersionPList函数,继续看这个函数:



看到有对这个文件的操作/System/Library/CoreServices/SystemVersion.plist,而且有个fopen。那我们运行Xcode,断点到这个fopen。

这个文件路径如下:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/CoreServices/SystemVersion.plist
我们打开看看,

那就明白咯,最终是访问这个文件,获取其中的ProductVersion,并通过sscanf解析出三个_GlobalMajor、_GlobalMinor和_GlobalSubminor三个数值。
sscanf 好古老呀,看到他,好像回到了刚学C语言的那一年。
阶段总结
从目前的分析来看,@available(iOS 11, *)最终将变为如下伪代码:
_GlobalMajor
_GlobalMinor
_GlobalSubminor
void _parseSystemVersionPList() {
char *path = ".../System/Library/CoreServices/SystemVersion.plist";
fp = fopen(path)
read from fp
parse ProductVersion
sscanf into _GlobalMajor,_GlobalMinor,_GlobalSubminor
}
BOOL ___isOSVersionAtLeast(major,minor,subminor) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_parseSystemVersionPList();
});
return compare major,minor,subminor with _GlobalMajor,_GlobalMinor,_GlobalSubminor
}
Clang 怎么处理
相关代码我们从这里搜https://github.com/llvm-mirror/clang/,
用于表示avaliable的AST:AvailabilitySpec。
https://github.com/llvm-mirror/clang/blob/master/include/clang/AST/Availability.h

还搜到了创建函数的代码,

具体我对llvm也不是太熟,就不细说了(也说不出来哈),感兴趣自行搜索啦。
再总结
了解了这个本质,以后用起来就更自信了哈。很有趣。
欢迎订阅 :)
