【iOS】 iOS不同操作系统兼容问题

Posted by 西维蜀黍 on 2017-05-15, Last Modified on 2021-10-02

1.背景

每隔一段时间,iOS都会更新最新的操作系统版本。因此对于App开发者,需要考虑不同iOS系统版本兼容的问题。

从Apple的官网上,我们可以获取到不同iOS操作系统版本的分布。

地址:https://developer.apple.com/support/app-store/

但是,由于官方对于数据的更新频率较慢,因此我们也可以参考以下数据:

虽然iOS版本碎片化问题比Andorid要好太多太多,然而客观上仍然存在一定的碎片化问题。

2.Xcode配置

在Xcode中,与iOS操作系统版本相关的主要包括Base SDKDeployment Target两个设置。

(1)Base SDK

Base SDK是指用来编译我们项目(App or 类库)的SDK(Software Development Kit)版本,每一个iOS操作系统版本都会对应一个SDK版本号。

这个SDK包含了开发者要用到的所有头文件、链接库的集合。如果Apple推出了新的API,也会增加在这个SDK里。

默认情况下,Xcode中创建的新工程总是使用当前已下载的最新的SDK(这样能保证你能够使用到Apple最新的API),而苹果会处理废弃的API。除非你有充分的理由,否则你应该使用这个默认的SDK版本。

(2)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(甚至更高)。

(3)Deployment Target与Base SDK关系

这里用一张图说明了deployment target设置为OS X v10.4base SDK 设置为OS X v10.6时的情况。 这时,应用可以无条件使用兼容10.010.4的API,有条件使用兼容10.510.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时根本还没有的语言特性。

3.各种问题

(1)注意依赖库的Deployment Target

我们所依赖的库的Deployment Target不应该高于应用的Deployment Target,否则可能发生难以排查的错误。

从第三方库开发者的角度来说,应该明确指出该库的Deployment Target设置。并在条件允许的情况下,将Deployment Target尽可能设置低一些,以增加适用性。

(2)运行操作系统判断

如果存在在特定操作系统上使用特定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)  {
  // ...
}

参考