iOS制作framework静态库-避坑指南

关于ios制作静态库,网上篇幅一大堆,这里重点介绍下遇到的坑。

一.如何制作framework

1.创建Framework项目
image.png
2.更改Xcode配置

2.1 修改支持版本以及平台


image.png

2.2 修改编译设置
链接类型,Mach-O Type 选择Static Library(静态库)

image.png

2.3 修改 Dead Code Stripping
将Build Settings中Link下面的Dead Code Stripping设置为NO。
注:Dead Code Stripping:舍弃无用代码,编译器会对一些没有引用的无效代码进行丢弃,这里我们可以设置未NO,尽量保证我们的源代码的完整性,避免一些额外的错误。


image.png

2.4.编译架构

image.png

2.5 修改Link With Standard Library
将Build Settings中Link下面的Link With Standard Libraries关闭,我想可能是为了避免重复链接


image.png
3.修改头文件的访问权限
image

或者从Build Phases中修改,将需要公开的头文件拖到Public下面

image.png

在Framework的头文件中导入需要公开的头文件【这里有坑,下文会讲】

image.png
4.合并静态库

至此就可以分别选择模拟器和Any iOS Device进行编译,得到模拟器下的架构和真机下的架构。然后通过lipo命令进行合并。下面简要介绍下相关lipo命令

// 查看库信息
lipo -info xxx.framework/xxxxFramework(库地址)
// 分离架构
lipo XXXX.framework/XXXX -thin arm64 -output XXXX.framework/XXXX-arm64
//-output 输出命令可以简写成 -o
//lipo XXXX.framework/XXXX -thin arm64 -o XXXX.framework/XXXX-arm64
// 合并架构
lipo -create XXXX.framework/XXXX-armv7 XXXX.framework/XXXX-arm64 -output XXXX.framework/XXXX
// 删除某个结构
lipo XXXX.framework/XXXX -remove i386 -output XXXX.framework/XXXX
5.添加生成Framework的脚本

鉴于以上手动合并库的形式较为low,可以创建一个脚本自动输出合并之后的库

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

#创建输出目录,并删除之前的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}"

lipo "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}" -remove arm64 -output "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}"

#合并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}"

选择Shell target,直接编译则生成Debug模式的Framework,Archive则生成Release模式的Framework

image.png

编译成功后将自动打开Framework所在的目录

image.png
二.避坑指南
坑1:公开的头文件import的时候使用双引号,外部使用的时候报找不到头文件

解决思路:一定要用尖括号的形式引用。因为打包成静态库之后,文件会打包在静态库资源包里,如果通过双引号引用导入,在静态库的项目里不会报错,但是外部使用进行编译的时候会从主项目中找头文件,那么肯定会报找不到头文件的error。

//一定要用#import <xxxx/xxxx.h> 引导编译器本模块寻找头文件
#import <aaaa/aaaa.h>
#import <bbbb/bbbb.h>
#import <cccc/cccc.h>
坑2:所有暴露为public的头文件中,存在部分头未暴露的文件引用。

比如aaaa.h设为public了,aaaa.h中引用了dddd.h,但是dddd.h并没有暴露出来,静态库内部编译没问题,但是外部使用会报sdk编译错误。

解决思路:暴露出来的头文件中,如有相关联的头文件也要一定暴露。或者将原本在.h文件中的引用放入.m文件中。

坑3:静态库中包含category,外部使用crash,报方法找不到。

解决方案(以下2点都要设置):
1.静态库中存在category时,在 Framework 文件中添加 target --> Build setttings --> linking --> Other linker flags 添加 -Objc,同时支持-all_load,保证分类能打包进去。
2.外部使用的时候,也需要在 target --> Build setttings --> linking --> Other linker flags 添加 -Objc,同时支持-all_load。

image.png

这里关于other Link Flags中的-ObjC,-all_load,-force_load,这里有详细说明:https://www.jianshu.com/p/f7b0aa817cff

坑4:库中有第三方库的引用,外部使用时会报存在重复的.o文件

解决方案:
方案A:通过rename修改类名,变成自己的私有框架
在打包成framework之前,在每个第三方框架的类名的前面,通过Xcode的rename操作对类名、系统分类的方法名,枚举,结构体进行统一修改,比如加一个前缀(随便你加什么)。 这是个细活,但是一劳永逸,这个框架以后就是属于你自己的了,随便你怎么去修改。跟其他工程中的类,也不会有任何冲突。


image.png

优点:一劳永逸,不会对外界产生影响。
缺点:会增加包的体积,同时rename是个细致活,尤其是对系统的一些分类的方法名、枚举值进行组个rename,比较麻烦。
方案B:制作静态库时对三方库只是添加单纯添加引用,不导入到模块中来。外部项目使用该的时候,由外部对该三方库进行手动导入或者pod引入。
优点:包体积小,不存在代码相同功能代码造成的代码冗余。
缺点:需要外部使用的时候添加三方库的依赖,中间可能会存在一些报错,接入成本较高。


image.png

参考:
https://www.jianshu.com/p/a80ce7d25ddf
https://www.jianshu.com/p/f7b0aa817cff
https://www.jianshu.com/p/e5726852bb82
https://www.jianshu.com/p/b92912216d65
https://www.jianshu.com/p/d79f6c866fdb

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容