背景
每隔一段时间,iOS都会更新最新的操作系统版本。因此对于App开发者,需要考虑不同iOS系统版本兼容的问题。
从Apple的官网上,我们可以获取到不同iOS操作系统版本的分布。
地址:https://developer.apple.com/support/app-store/
但是,由于官方对于数据的更新频率较慢,因此我们也可以参考以下数据:
虽然iOS版本碎片化问题比Andorid要好太多太多,然而客观上仍然存在一定的碎片化问题。
Xcode配置
在Xcode中,与iOS操作系统版本相关的主要包括Base SDK
和Deployment Target
两个设置。
Base SDK
Base SDK
是指用来编译我们项目(App or 类库)的SDK(Software Development Kit)版本,每一个iOS操作系统版本都会对应一个SDK版本号。
这个SDK包含了开发者要用到的所有头文件、链接库的集合。如果Apple推出了新的API,也会增加在这个SDK里。
默认情况下,Xcode中创建的新工程总是使用当前已下载的最新的SDK(这样能保证你能够使用到Apple最新的API),而苹果会处理废弃的API。除非你有充分的理由,否则你应该使用这个默认的SDK版本。
Deployment Target
Deployment Target
是指编译出的程序最低可以在哪个版本的操作系统上运行。
比如我设置成iOS 10.2
,那么编译出的App or 类库,最低可以在iOS 10.2的平台上运行(前提是未使用高于10.2的API)。如果这个App发布到了AppStore,那么若低于 10.2
版本的设备下载或安装这个App,会被自动阻止。
事实上,如果手动将这个值设置成iOS 8.0以下的话,Xcode 会发出警告(印象中是从 Xcode 8开始出现这个警告的),建议将Deployment Target
设置为iOS 8.0。
结合【1.背景】中提到的iOS 操作系统版本分布统计,iOS 8.0以下的占有量其实也是非常非常低的。
截止2017.5.1,除了个别用户量非常非常大的App(微信设置为7.0,支付宝设置为7.0),大部分App都至少将其设置为8.0(甚至更高)。
Deployment Target与Base SDK关系
这里用一张图说明了deployment target
设置为OS X v10.4
, base SDK
设置为OS X v10.6
时的情况。
这时,应用可以无条件使用兼容10.0
到10.4
的API,有条件使用兼容10.5
到10.6
的API(必要时进行运行时判断)。
让我们更深入的剖析这张图:
特性:
Deployment target
决定了你可以无条件使用的所有函数和系统调用的范围Base SDK
决定了你可以挑选的一些最新的功能 or API 的范围
因此:
- 如果你在App中使用连
Base SDK
都不支持的函数,那会产生一个编译错误 - 如果你使用了在文档中被标明了在
Base SDK
对应操作系统下中是deprecated
的函数,那会产生一个编译警告 - 如果你使用了
Base SDK
里的可选函数(),然后编译出App。可是安装到低SDK版本的iPhone上,该函数只是一个空指针。
最终,我们得出两者的关系:Base SDK
的版本大于等于 Deployment Target
的版本。
这里仍然要提醒一点:只要用到的类、方法在当前Base SDK版本里存在,就可以编译通过。
但是编译通过
并不意味着就一定能执行成功
。
因为,如果运行这个App设备对应的系统版本低于这些类 or 方法的最低版本要求,App可能就会Crash。这也解释了有条件使用兼容10.5到10.6的API(必要时进行运行时判断)
的含义。即,在使用某些兼容范围没这么广的API时,需要做相应判断,在条件允许的情况下,才可以使用。
当然啦,说得容易,实践起来还是略微困难的。因此,有一个排查Bug的tip: 如我们采用了第三方库来监测线上App崩溃的情况,如果某个崩溃只在某个崩溃只在特定操作系统上发生,那么问题很有可能有API兼容性有关。
之所以会存在【有条件使用某些API】这个需求,是因为Apple官方会不定时废弃一些API。
已废弃(Deprecated)的API指的是那些已经过时且在将来某个时间点会被移除的方法或类。
通常,Apple在引入一个更优秀的API后,就会把原来的API废弃掉。新引入的API通常意味着可以更好的发挥新硬件或操作系统的性能,或者使用了一些在构建原有API时根本还没有的语言特性。
各种问题
注意依赖库的Deployment Target
我们所依赖的库的Deployment Target
不应该高于应用的Deployment Target
,否则可能发生难以排查的错误。
从第三方库开发者的角度来说,应该明确指出该库的Deployment Target
设置。并在条件允许的情况下,将Deployment Target
尽可能设置低一些,以增加适用性。
运行操作系统判断
如果存在在特定操作系统上使用特定API的需求,我们可以借助系统预定义的宏
,也可以在运行时判断当前系统版本。
a.使用宏
**宏定义只在编译时起作用。**换句话说,被宏包起来的代码是否被执行,在编译的时候就确定了。
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
NSLog(@"swsmile");
#endif
用到的宏:
- __IPHONE_OS_VERSION_MAX_ALLOWED: 值等于
Base SDK
- __IPHONE_OS_VERSION_MIN_REQUIRED: 值等于
Deployment Target
b.运行时判断当前系统版本
与宏只在编译时起作用的特性不同,我们还可以在运行时判断当前系统版本:
# 判断当前系统版本是否大于8.0
if ([UIDevice currentDevice].systemVersion.floatValue > 8.0f) {
// ...
}
参考
- App Distribution Guide
- SDK and Deployment Target
- Configuring a Project for SDK-Based Development
- iOS开发之多系统版本兼容
- SDK Compatibility Guide
- Everything You Need to Know about iOS and OS X Deprecated APIs
- 解决多版本SDk的兼容问题