iOS项目中SDK的开发之SDK开发(一)

一、什么是 SDK?

SDK 全称 Software Development Kit,广义上的 SDK 是为特定的软件包、软件框架、硬件平台、操作系统等建立应用程序时所使用的开发工具的集合(在 iOS 项目中,SDK 也被称为库)。

在 iOS 开发或 Android 开发中,不可避免会需要使用第三方工具提升产品的开发效率,比如用于消息推送的极光,用于第三方支付与登录的支付宝,微信等等。但大多数商用产品都不会直接给出源码(可能只有为爱发电的开源项目才会无私提供源码),而我们在开发 App 时就需要将这些第三方 SDK 集成在我们的项目之中。简单来说SDK就是提供开开发者的一个工具,使其实现所需的功能。

二、SDK 设计的基本原则

一款好用且设计充分的 SDK 必须要遵循以下 4 条基本原则,即:

🐡 1、SDK 安全,稳定

考虑到 SDK 是需要嵌入到 App 里面去的,所以 SDK 最重要的特性就是安全性,不会因为乱开放接口而导致 App 数据泄露;其次重要的是 SDK 的稳定性, SDK 的 Crash 如果没有被捕获进行处理,则会导致应用彻底崩溃(这样就会导致第三方接入的 App 体验性非常差),甚至会直接导致接入方的用户流失;

🐡 2、统一的开发规范

对于 SDK 开发规范来说,统一的命名规范很重要,最好的状态是“接入方看到接口命名就能知道是哪家厂商的 SDK”,换句话说就是 SDK 的命名规范统一,形成自己公司的品牌效应,此外也方便开发者进行接入使用。此外也需要具有自己的编码规范,你可以在网上找到大厂的规范模板,并通过借鉴整理出属于自己的规范,从而尽早统一代码风格;

🐡 3、Library 小而精

小是指要避免造成接入方的App增加很大,不然会引起接入方的不满,甚至下架。精是指功能要专注,比如极光推送,就是专注推送相关的功能;

🐡 4、不依赖第三方 SDK

这个也很好理解,SDK 中如果又依赖其他第三方 SDK, 不仅会导致 SDK 的体积变大,也会影响接入方集成 SDK 的相关成本。

三、在 iOS 环境下开发 SDK

🐹 1. iOS 环境下的 SDK

如同上文所说,在 iOS 开发中,我们将 SDK 称为“库”,我们是这样对其定义的:

一般是给应用提供通用服务的,非独立运行的程序集合;
一般都是编译过的,方便使用。

我们会根据库的调用方法分为“静态库”和“动态库”两种:

🌴 静态连接:一般是指在创建应用程序的时候,将库集成进去,这样做的好处就是应用程序包自身可以独立运行,而不好的地方就是包会略显臃肿,库不能共享(静态库经常以 .a 结尾);

🦋 动态连接:创建应用的时候只约定好与库之间的调用关系,而不彻底将库包集成进应用。这样在应用运行时,需要运行环境中提供库,并且连接装载。优劣与静态库相反,动态链接库需要库环境,但由于本身不集成库内容,会比较小,同时也为和其他应用共享库的使用提供了可能(常见的动态库是 Windows 下的 .dll,Linux 下的 .so,Mac 下的 .dylib/.tbd)。

🐯 !!!特别注意:平时我们经常说的 Framework (in Apple) 是 Cocoa/Cocoa Touch 程序中使用的一种资源打包方式,可以将代码文件、头文件、资源文件、说明文档等集中在一起,方便开发者使用。也就是说我们的 Framework 其实是资源打包的方式,和静态库、动态库的本质是没有什么关系。

🐹 2. 静态库和动态库的区别

如果说要找出静态库与动态库的区别,那可以从文件链接(每个源代码模块独立编译,然后按照需要将他们组装起来,这个组装模块的过程,就是链接)的角度进行解释:

🌴 静态库:链接时会被完整的复制到可执行文件中,所以如果两个程序都用了某个静态库,那么每个二进制可执行文件里面,都会含有这份静态库的代码;

🦋 动态库:链接时不复制,而是在程序启动后动态加载,然后再进行符号决议(符号绑定)。理论上动态库只存在一份就可以了。其他的程序都可以动态链接到这个动态库上面,从而节省内存(内存中只有一份动态库)。另外一个好处是,由于动态库并不绑定到可执行程序上,所以我们想升级这个动态库就很容易,windows和linux上面一般插件和模块机制都是这样实现的。

🆚 动、静态库简单对比:

🐹 3. 了解 iOS 的动态库(即被阉割的动态库)

有一个背景知识需要注意,iOS 官方规定不允许存在动态库,并且所有的 IPA 都需要经过 Apple 的私钥加密后才能用,即使你用了动态库也会因为签名错误而无法加载(越狱和非 App Store 除外)。于是这就把开发者自己开发动态库这件事变成为了天方夜谭。

iOS8 之前的 iOS 应用都是运行在沙盒当中的,不同程序之间不能共享代码,并且 iOS 又是单进程运行的(也就是某一时刻只有一个进程在运行),那么即使你写个共享库也无法共享给他人。而动态下载代码又是被苹果官方明令禁止的,也就是说动态库的优势完全无法发挥,所以动态库也就没有存在的必要了。

但是这一切问题都随着 iOS8 发布之后的 App Extesion 特性, Swift 的诞生发生了奇妙的改变。由于 iOS 主 App 需要和 Extension 共享代码,Swift 语言机制也需要动态库,于是苹果后来提出了 Embedded Framework,这种动态库允许 APP 和 App Extension 共享代码(动态库的生命被限定在一个APP进程内)。

更简单的解释:虽然提供了动态库,但这是被阉割的动态库。

尽管如此,这种动态库(Embedded Framework) 和系统的 UIKit.Framework 还是有很大区别的。传统的动态库是给多个进程使用的,而这里的动态库(Embedded Framework)是给单个进程里面多个可执行文件用的。

系统的 Framework 不再需要拷贝到目标程序中,我们自己做出来的动态库(Embedded Framework) 哪怕是动态的,最后也还是要拷贝到 App 中(App 和 Extension 的 Bundle 是共享的)。所以苹果没有直接把这种 Embedded Framework称作动态库而是叫 Embedded Framework。

上面提到的 Swift 也有原因,在 Swift 的项目中如果要在项目中使用外部代码,可选的方式只有两种,一种是把代码拷贝到工程中,另一种是用动态 Framework。使用静态库是不支持的。

这个问题的根本原因是, Swift 的运行库没有被包含在 iOS 系统中,反而会被打包进 App 中(这也是造成 Swift App 体积大的原因),静态库会导致最终的目标程序中包含重复的运行库。

🐹 4. 动态库、静态库、framework是什么关系?

🐡 静态库/动态库

库是已经编译完成的二进制文件。

  • 代码需要提供给外部使用又不想代码被更改,就可以把代码封装成库,只暴露头文件以供调用。
  • 希望提高编译速度,可以把部分代码封装成库,编译时只需要链接。
  • 库都是需要链接的,链接库的方式有静态和动态,所以就产生了静态库和动态库。
🐡 framework

framework其实是一种文件的打包方式,把头文件、二进制文件、资源文件封装在一起,方便管理和分发。所以动态库和静态库的文件格式都会有.framework

  • Dynamic Framework动态库,系统提供的 framework 都是动态库。比如 UIKit.framework,具有所有动态库的特性。

  • Static Framework静态库,开发者可以制作。可以理解为静态库 = 头文件 + 资源文件 + 二进制代码,具有静态库的属性。

  • Embedded Framework也是动态库的一种,用户可以制作。系统的Framework不需要拷贝到目标程序中,Embedded Framework最后需要拷贝到APP中。他具有部分动态特性,可以在 Extension可执行文件目标APP 之间共享。

  • XCFramework是苹果官⽅推荐的、⽀持的文件格式。支持 xcode11 以上,可以提供多个不同平台的分发二进制文件,xcode会自动判断你需要编译的ipa包是什么架构,使用的时候就不用通过脚本剥离不需要的架构体系。

🐹 5. 动态库和静态库链接到主程序以后放在什么位置?

🐹 6. CocoaPods 中的动态库静态库使用

🐡 静态库使用

默认情况下, 当我们在 Podfile 文件中写下:

platform :ios, '12.0'
target 'MyDynamicLibrary' do
  #use_frameworks!
  pod 'AFNetworking', '~> 4.0'
  pod 'SDWebImage', '~> 5.0'
end

的时候, cocoapods 默认会使用静态库, 我们可以在 Products 文件夹中看到编译出的 .a 文件

在项目的 .app 中, 我们可以看到静态库被编译进入可执行文件 (mach-o 文件), 导致文件大小为 1.5 M

🐡 动态库使用

cocoapods 提供了 use_frameworks! 选项让我们可以以 .framework 的形式导入第三方库, cocoapods 默认我们开启了此选项后在 .framework 文件夹中放的是动态库, 因此我们可以在 Podfile 中加入 use_frameworks! 来达到引入动态库的效果, 如下:

platform :ios, '12.0'

target 'MyDynamicLibrary' do
  use_frameworks!
  pod 'AFNetworking', '~> 4.0'
  pod 'SDWebImage', '~> 5.0'

end

然后经过 pod update 之后, 结果如下:

cocoapods 编译生成的结果文件已经变为了 .framework 文件夹

再来看项目结果文件 .app:

我们可以看到

  • 由于动态库未被编译进入可执行文件 (mach-o 文件), 导致文件大小减小到 57 kb
  • 多了一个 Frameworks 文件夹用于存放 .framework 文件

posted on 2018-08-21 14:19  梁飞宇  阅读(1505)  评论(0)    收藏  举报