从初代 iPhone 推出到现在为止已经接近 10 年,由于 iOS 系统的良好优化,即使是多年前发布的 iPhone 手机现在也能很流畅的使用,这对用户来说是一件大好事,然而对开发者来说,由于我们想使用新系统新处理器提供的特有框架,必然要对这部分旧设备的手机提供限制,也就是设备兼容性的问题。最近在开发的 app 打算使用 Metal 框架,而 Metal 框架不仅对 iOS 系统有限制(>=iOS 8),而且对硬件也有一定的要求:它只能运行在 Apple A7 开始的 arm64 架构处理器上。因此,这有必要通过一些设置来限制 app 所能兼容的设备。

查阅官方文档 iOS Device Compatibility Reference 可知:Apple Store 或 iTunes Store 会通过查看 app Info.Plist 文件里的 UIRequiredDeviceCapabilities 键值来对想要安装这个 app 的用户做出限制。这个键的值接受数组或字典作为它的值,如果使用字典,则对这个字典中的每一个键值对来说,键表示想要设置的功能名称,用布尔值来表示是否开启这个功能;如果使用数组,则数组里的每一项都表示 app 所需要的功能。

在苹果的另一份文档 Information Property List Key Reference 里具体谈到了某个功能键的意义以及它所支持的最小系统版本。

对于我的需求来说,设置 metal 正好可以满足我的我的需求。因此这里我们来写个 demo 看看具体情况。

打开 Xcode 新建一个 Single View App,项目名为 TestLimitCompatibility,创建好之后修改项目的 Deployment Target9.0,修改 Info.Plist 里的 UIRequiredDeviceCapabilitiesmetal

然后将这个 app 打包并将生成的 ipa 文件修改后缀为 zip,解压。使用 lipo 命令:

$ lipo -info TestLimitCompatibility.app/TestLimitCompatibility
Architectures in the fat file: TestLimitCompatibility are: armv7 arm64

查看 app 大小:

由于使用 Swift 并且目前为止 Swift 并没有实现 ABI Stable,因此生成的包里包含了 Swift 标准库和 UIKit 等,导致一个模板 app 就有 31.7 MB 的大小。我们看一下其中一个库文件的组成:

$ lipo -info libswiftCore.dylib
Architectures in the fat file: libswiftCore.dylib are: armv7 armv7s arm64

可以看到,由于我们使用默认设置,项目打包后的包里有 armv7armv7sarm64 三种架构的库文件,他们合并为一个 fat binary

然而,目前来说,支持 Metal 的 CPU 仅仅为 arm64 架构,因此,如果打出来的包里只有 arm64 架构就更有利于减小包大小了。打开项目的 Build Settings 选项,可以看到有多个涉及到 Architecture 的设置

对于打包来说,苹果使用 ArchitecturesValid Architectures 取交集的方式来决定打包后的 app 里包含哪种 CPU 架构的库。这里,将 ArchitecturesValid Architectures 都设置为 arm64 并打包。相同的,解压查看生成的 app:

$ lipo -info TestLimitCompatibility.app/TestLimitCompatibility
Non-fat file: TestLimitCompatibility is architecture: arm64

可以看到,我们的 app 打包出来的包已经只有 arm64 架构了。相应的,app 大小也有变化

好像只小了那么一点?我们再看看系统库:

$ lipo -info libswiftCore.dylib
Architectures in the fat file: libswiftCore.dylib are: armv7 armv7s arm64

系统库依然包含了三种架构,回想打包时为了加快速度我选择不使用 bitcode 技术,导致 Xcode 不能准确的判断 app 实际想要支持的设备类型,因此它只能将三种架构全部打包进 app 内。我们再看看启用 bitcode 打包的app

只有 7.8 MB 大小!再看看系统库的架构:

$ lipo -info libswiftCore.dylib
Non-fat file: libswiftCore.dylib is architecture: arm64

果然,启用 bitcode 后打出来的包只有 arm64 架构了。配合苹果的 App Thining 技术,可以根据不同的设备和系统生成对应的更精简的包,对用户和苹果自己来说体验都更好。

结论

设置 Info.Plist 里的 UIRequiredDeviceCapabilities 以及 Build Settings 里的 ArchitecturesValid Architectures 可以更精确的设置 app 想要支持的设备类型,但是由于这只是一个 demo 项目,这种方式是否能够通过审核还没有经过检验(StackOverflow 上有类似的问题,答案都是 NO),根据我们 app 的审核结果我会更新这篇博客,如果读者里有用类似解决方法的可以一起交流。

参考