这里输入引用文本本文记录一下,在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是否是通平台的包 ##总结
- 开发是一个不断踩坑和填坑的过程,制作framework在iOS开发中是一个比较小众的需求,我会持续的把我遇到的蛋疼的事儿记录下来,以供来着参考,有更好的方法也欢迎讨论
- 在SDK制作过程中用workspace的方法管理project是一个很有效率的方法,这是我们团队的adams总结使用的,通过这种方法可以源码project和demo project放在一起,可以同时在一个地方搞定两件事:SDK开发和framework集成
- 学会制作framework仅仅是万里长征第一步,framework给到第三方集成过程中会遇到各种麻烦,这就需要不断的总结和沉淀
- 本文目前为止制作的是动态的framework,如果你的需求是做一个静态的framework,变化很简单,你只需要改变building settings -- > Mach-o type为static即可
- 默认的iOS develop target是当前最新版本的,你的framework需要兼容低版本的话就需要在build settings --> development --> iOS develop target中设置你的目标版本
来源:oschina
链接:https://my.oschina.net/u/2304335/blog/786636