探索 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也不是太熟,就不细说了(也说不出来哈),感兴趣自行搜索啦。
再总结
了解了这个本质,以后用起来就更自信了哈。很有趣。
欢迎订阅 :)