xcode 8 /iOS10下静态库和动态库的区别 && framework的制作

雨燕双飞 提交于 2019-12-02 04:14:29

这里输入引用文本本文记录一下,在SDK开发完成后,如何高效率制作framework。

##iOS关于静态库、动态库的一些基本概念和理解误区 ###1. 库 库是源代码经过编译,形成的二进制代码,别人项目中使用我们的库的时候,库在参与编译的时候,直接link就OK了,按照link的方式,可以把库分为静态库动态库 ###2. 静态库 静态库在编译的时候会被直接拷贝一份,复制到目标程序里,这段代码在目标程序里就不会再改变了。

一般以**.a** 和 .framework为文件后缀名

这种做法是牺牲应用“体量”来节省编译时间。 ###3. 动态库 与静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。

动态库的优点是,不需要拷贝到目标程序中,不会影响目标程序的体积,而且同一份库可以被多个程序使用(因为这个原因,动态库也被称作共享库)。

同时,编译时才载入的特性,也可以让我们随时对库进行替换,而不需要重新编译代码。动态库带来的问题主要是,动态载入会带来一部分性能损失,使用动态库也会使得程序依赖于外部环境。如果环境缺少动态库或者库的版本不正确,就会导致程序无法运行

以**.tbd**(之前叫.dylib) 和** .framework** 为文件后缀名

苹果系统为我们提供了很多动态链接库,我们可以在我们项目工程中查看一下

输入图片说明输入图片说明 ###4. Framework Framework 是一种打包方式,将库的二进制文件,头文件和有关的资源文件打包到一起,方便管理和分发。

Framework只是一种打包方式,其本身和静态、动态无关!

Cocoa Touch Framework 的实际内容为 Header + 动态链接库 + 资源文件

###5. 对Framework认识的误区 误区①:.framework是动态库,.a是静态库,前面已经讲过,不再赘述

误区②:有人说“自定义的动态库苹果审核不通过”,那我打的framework是不是通不过审核?

任何没有时间前提的结论都是耍流氓!!!

在 **iOS 8 / iOS6之前,**iOS 平台不支持使用动态 Framework,开发者可以使用的 Framework 只有系统的framework,这种限制可能是出于安全的考虑

换一个角度讲,因为 iOS 应用都是运行在沙盒当中,不同的程序之间不能共享代码,同时动态下载代码又是被苹果明令禁止的,没办法发挥出动态库的优势,实际上动态库也就没有存在的必要了

但是,码农总是喜欢折腾的,一方面骨子里面有一种“你不让我做我偏要做的倔强”,另一方面用framework确实比用.a加头文件的方式简单,所以这一时期的开发者用了很多**“奇淫技巧”来制作framework**,这就有了Fake Framework 和 Real Framework的区分

华丽的分割线------

输入图片说明

在 **iOS 8 / iOS6后,iOS平台添加了动态库的支持,同时, Xcode 6 也原生自带了 Framework 支持,注意,前后两个维度的不同是两件事,不要混淆。。。

那么,为什么 iOS 8 要添加动态库的支持?

主要的理由大概就是 Extension 的出现。Extension 和 App 是两个分开的可执行文件,同时需要共享代码,这种情况下动态库的支持就是必不可少的了。但是这种动态 Framework 和系统的 UIKit.Framework 还是有很大区别;还有就是为了支持swift

虽然同样是动态框架,但是和系统 framework 不同,app 中的使用的 Cocoa Touch Framework 在打包和提交 app 时会被放到 app bundle 中(App 和 Extension 的 Bundle 是共享的),运行在沙盒里,而不是系统中。也就是说,不同的 app 就算使用了同样的 framework,但还是会有多份的框架被分别签名,打包和加载,因此苹果又把这种 Framework 称为** Embedded Framework**,也正是代码签名机制,通过AppStore发布的APP是无法通过替换服务端下发framework的方式来进行热更新!

##项目遇到的痛点 项目组本来是使用脚本结合xctool(Facebook的一个开源项目)的方式来打包制作framework,但在升级xcode8之后,xctool在xcode8下出现了bug,而且Facebook并没有在第一时间维护这个项目,一时间一脸懵逼。。。

由于项目时间紧,无奈之下只能采用了一个临时解决方案,就是重新下载一个xcode7版本,让xctool在xcode7环境下打包,但这绝非长久之计!

大概一个月之后,虽然xctool项目更新了对 xcode8 以及 xcode8.1 的支持,但还是决定放弃原有的方案,原因有二,首先,Facebook有着很好的开源精神,但是也是出了名的喜欢撒手不管的角儿,万一哪天停止对这个工具的维护,我怎么办!!!用一种简单、稳定、可靠的解决方案是我们急需的;第二,xctool有很强大的功能,但我们在项目中只用了其中一小块,有点杀鸡用牛刀的意思,项目中很多冗余。 ##解决方案 替代方案就是用xcode提供的aggregate结合脚本来制作framework ##制作过程 ###1. 用workspace管理SDK project 新建一个workspace,命名为TestSDKWorkspace 输入图片说明

现在生成的workspace还是一个空的什么都没有,接着往里面添加我们的静态库project

点击左下角的**+**号

输入图片说明

project类型选择cocoa touch framework,取名为TeskSDK 输入图片说明

在SDK项目中添加测试源代码,写一个Test类,对外提供testMethod方法,注意,这个类要添加到SDK target中去!

#import <Foundation/Foundation.h>
@interface Test : NSObject
- (void)testMethod;
@end

#import "Test.h"
@implementation Test
- (void)testMethod{
    NSLog(@"--------");
    NSLog(@"this is a test method");
    NSLog(@"--------");
}
@end

我们决定对外暴露Test.h,将其添加至接口文件中

输入图片说明

此时,我们编码准备工作已经做好了,command+B,我们可以在product文件夹下面看到有一个TestSDK.framework从红变黑,这是不是意味着我们需要的framework已经大功告成了呢?

no!!!我们此时只是在特定平台下编译的,得到的也只是这个平台下的framework,我们在文件夹中查看如下图

输入图片说明

这就意味着我们要打同平台的包,需要经过多次编译,最后再去合成,这太麻烦了 ###2. 用“Aggregate+脚本”制作framework 选择TARGETS点击然后添加Aggregate

输入图片说明

添加如下脚本到Aggregate target对应的build phases-->run script中

#!/bin/sh
#要build的target名
TARGET_NAME=${PROJECT_NAME}
if [[ $1 ]]
then
TARGET_NAME=$1
fi
UNIVERSAL_OUTPUT_FOLDER="${SRCROOT}/${PROJECT_NAME}_Products/"

#创建输出目录,并删除之前的framework文件
mkdir -p "${UNIVERSAL_OUTPUT_FOLDER}"
rm -rf "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework"

#分别编译模拟器和真机的Framework
xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

#拷贝framework到univer目录
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework" "${UNIVERSAL_OUTPUT_FOLDER}"

#合并framework,输出最终的framework到build目录
lipo -create -output "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${TARGET_NAME}.framework/${TARGET_NAME}"

#删除编译之后生成的无关的配置文件
dir_path="${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/"
for file in ls $dir_path
do
if [[ ${file} =~ ".xcconfig" ]]
then
rm -f "${dir_path}/${file}"
fi
done
#判断build文件夹是否存在,存在则删除
if [ -d "${SRCROOT}/build" ]
then
rm -rf "${SRCROOT}/build"
fi
rm -rf "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator" "${BUILD_DIR}/${CONFIGURATION}-iphoneos"
#打开合并后的文件夹
open "${UNIVERSAL_OUTPUT_FOLDER}"

输入图片说明 ###3. 生成framework 在target中选择TestAggregate,build device选择Generic Device,然后 command + B 输入图片说明

由于脚本末尾添加了open命令,编译结束之后会打开framework所在的文件夹

至此,SDK生成完毕,我们需要写个demo工程验证一把 ###4. 验证framework 在workspace中新建一个demo工程,然后把TestSDK.framework引入

输入图片说明

在viewController中调用SDK方法

输入图片说明

从控制台的输出可以看出,我们已经成功的调起TestSDK中的方法。

我的另外一篇文章解决引用百度地图sdk冲突问题,其中介绍了如何查看framework的内部结构,你可以用里面的方法来查看我们打出来的framework是否是通平台的包 ##总结

  1. 开发是一个不断踩坑和填坑的过程,制作framework在iOS开发中是一个比较小众的需求,我会持续的把我遇到的蛋疼的事儿记录下来,以供来着参考,有更好的方法也欢迎讨论
  2. 在SDK制作过程中用workspace的方法管理project是一个很有效率的方法,这是我们团队的adams总结使用的,通过这种方法可以源码project和demo project放在一起,可以同时在一个地方搞定两件事:SDK开发和framework集成
  3. 学会制作framework仅仅是万里长征第一步,framework给到第三方集成过程中会遇到各种麻烦,这就需要不断的总结和沉淀
  4. 本文目前为止制作的是动态的framework,如果你的需求是做一个静态的framework,变化很简单,你只需要改变building settings -- > Mach-o type为static即可
  5. 默认的iOS develop target是当前最新版本的,你的framework需要兼容低版本的话就需要在build settings --> development --> iOS develop target中设置你的目标版本
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!