一、创建Framework工程(环境:Xcode10, iOS12)
command+shift+n 生成一个Cocoa Touch Framework,并将开发者自己的类加入到工程中。
二、头文件处理
需要公开给别人的.h文件放到Public中,把不想公开的.h文件放到 Project中。
项目中公开的需要引入到文件XLBaseProjectFramework.h中,如
#import
#import
三、Build Active Architecture Only 设置(不同机型的架构不一样,设置为NO适配不同机型, YES为当前机型)
默认:debug:NO, release:YES
四、Architectures支持
指定工程被编译成可支持哪些指令集类型,而支持的指令集越多,就会编译出包含多个指令集代码的数据包,对应生成二进制包就越大,也就是ipa包会变大。一般做向下兼容原则,只编译了armv7s。
五、Mach-O配置
Executable:应用的主要二进制
Dylib Library:动态链接库(又称DSO或DLL)
Static Library:静态链接库
Bundle:不能被链接的Dylib,只能在运行时使用dlopen( )加载,可当做macOS的插件
Relocatable Object File:可重定向文件类型
六、工程打包成 Framework方案。
执行到第四步时,command+b编译就制作出了Framework,但是要想在手机、模拟器上使用,还需在对应环境编译并合并。
(一)方案一:手动合并
lipo命令合并
(二)方案二:通过Aggregate,自动合并
File --> New --> File... --> Cross-platform --> other --> Aggregate
1、创建一个Aggregate
2、加入脚本命令
#!/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_NA ME}" "${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
七、通用配置
(一)设置好最低支持的iOS系统版本
(二)SDK的plist增加配置键值对
1、允许http请求
2、隐私权限相关说明文字( 涉及apple审核政策5.1.1)
参考知名app描述:
QQ:
获取麦克风权限: 请点击”好”以允许访问.若不允许,你将无法使用视频通话、发送语音消息或录制视频等功能.
获取相机权限: 请点击”好”以允许访问.若不允许,你将无法使用拍照、录制视频、扫一扫等功能.
获取定位权限: 若不允许,你将无法使用位置发送、附近的人、群与活动、空间挂件、吃喝玩乐等功能.
获取相册权限: 若不允许,你将无法给好友发送或在空间上传本地相册图片及视频内容.
获取通讯录权限: 请点击”好”以允许访问.若不允许,你将无法使用通讯录联系人、添加手机联系人等功能.
获取Siri权限: 您的部分”QQ”数据将发给Apple以处理您的请求.如果不允许,你将无法使用Siri唤起QQ
获取活动与体能训练记录权限: 请点击”好”以允许访问.若不允许,你将无法同步运动健康数据至QQ运动.
微信:
获取麦克风权限: 如果不允许,你将无法在微信中发送语音消息,或进行音频通话.
获取相机权限: 如果不允许,你将无法再微信中拍摄照片和视频,也无法使用视频通话、扫一扫等功能
获取相册权限: 如果不允许,你将无法发送系统相册里的照片给朋友.
获取定位权限: 如果不允许,你将无法在聊天中共享你的位置,也无法使用”摇一摇”和”附近的人”等基于位置的服务.微信还会使用这些信息提供关联搜索结果,并在你的注册或登录微信时保障你的账户安全
获取通讯录权限: 如果不允许,微信将无法推荐通讯录中的朋友给你.微信仅使用特征码用于匹配识别,不会保存你的通讯录内容
获取Siri权限: 您的部分”微信”数据将发给Apple以处理您的请求.如果不允许,您将无法通过Siri发送微信消息
获取活动与体能训练记录权限: 如果不允许,你将无法参与微信运动排行榜.
上传通讯录提示: 微信将上传手机通讯录至微信服务器以匹配及推荐朋友.(上传通讯录仅用于匹配,不会保存资料,亦不会用作它用)
淘宝:
获取定位权限: “手机淘宝”想访问您的位置,为了提供附近的商品、店铺及优惠资讯
获取相机权限: “手机淘宝”想访问您的相机,为了帮您扫描二维码或者商品和互动等功能
获取麦克风权限: “手机淘宝”想访问您的麦克风,为了帮您用语音搜索卖家商品等功能
获取相册权限: “手机淘宝”想访问您的照片,为了帮您实现晒买家秀等上传照片的功能
支付宝:
获取定位权限: 若不允许,你将无法在支付宝中使用商家服务、本都生活、发送地理位置等定位相关功能
获取通讯录权限: 支付宝讲上传通讯录至服务器以匹配及推荐朋友,给朋友转账更方便(未经您同意,通讯录信息不会用于其他用途).
获取相册权限: 若不允许,你将无法在支付宝中发送及保存照片
获取相机权限: 若不允许,你将无法在支付宝中使用扫码、刷脸、拍照及拍摄小视频等功能.
获取麦克风权限:若不允许,你将无法在支付宝使用语音消息、小视频及语音搜索等功能.
(三)Build Phases配置
1、Link Binary With Libraries里添加项目的依赖库
2、Copy Bundle Resources里添加项目中使用到的资源文件,如图片、xib文件、plist文件等
3、 Headers里有三个选项是 Public、Private、Project;
把需要公开给别人的头文件拖到Public 中,
把不想公开的(即隐藏的)头文件拖到Project中。
(Private下的头文件依然是可以暴露出来的,因此名字可能有些误导。事实上Project下的头文件对你的工程来说才是“私有”的,因此,一般的头文件或者在Public或者Project下)
(四)Bulid Settings配置
1、Prefix Header 创建pch文件索引
名字不能用默认名,一般都通过下划线加上项目名来命名,如PrefixHeader_SDK.pch
索引为:$(PROJECT_DIR)/$(PRODUCT_NAME)/PrefixHeader_XLCommonKitSDK.pch
2、Precompile Prefix Header设置为YES,pch会被预编译,预编译后的pch文件会被缓存起来,从而提高编译速度
3、Enable Bitcode 设置为NO来关闭该功能。
Bitcode有一致性要求,这就意味着工程开启Bitcode之后必须要求所有打进Bundle的Binary都需要支持Bitcode,也就是说我们依赖的静态库都要含有Bitcode的,不然会报错。
(如果你要开启Bitcode,开启之后需要特别注意崩溃定位的问题:
由于最终的可执行文件是Apple自动生成的,同时产生新的符号表文件,所以我们使用原本打包生成的dSYM符号化文件是无法完成符号化的。
所以我们需要在上传至App Store时需要勾选Include app symbols for your application to receive symboilcated crash logs from Apple,勾选之后Apple会给我们生成dSYM,
然后就可以在Xcode -> Organizer或者iTunes Connect中下载对应的dSYM来进行符号化了)
(bitcode是被编译程序的一种中间形式的代码。包含bitcode配置的程序将会在App store上被编译和链接。 bitcode允许苹果在后期重新优化程序的二进制文件,而不需要重新提交一个新的版本到App store上。)
4、Other Linker Flags
当你的SDK项目使用了分类,别人在用你的库时需要在里面加入-ObjC参数。
<1>ObjC:加了这个参数后,链接器就会把静态库中所有的Objective-C类和分类都加载到最后的可执行文件中,
如果使用了分类就要加这个参数。
<2>all_load:会让链接器把所有找到的目标文件都加载到可执行文件中,
但是千万不要随便使用这个参数!
假如你使用了不止一个静态库文件,然后又使用了这个参数,
那么你很有可能会遇到ld: duplicate symbol错误,因为不同的库文件里面可能会有相同的目标文件,
所以建议在遇到-ObjC失效的情况下使用-force_load参数。
<3>force_load:所做的事情跟-all_load其实是一样的,
但是-force_load需要指定要进行全部加载的库文件的路径,
这样的话,你就只是完全加载了一个库文件,不影响其余库文件,
按需加载。
5、Build Active Architecture Only
<1>NO, 这个配置的作用是生成所有平台的二进制文件
<2>YES,只是为了在Debug的时候速度更快,它只编译当前的architecture 版本
6、Architectures
点击Other..增加armv7s支持,
不过如果不需要支持iPhone5和iPhone5C的话,则不需要加(同样的,当少了一个平台时,编译出来库的大小就会相应的变小)
7、Mach-O Type
设置为Static Library;我们创建的framework默认是动态库,所以我们要将其改为静态库
8、Product Name
这里设置的名称是编译出来的framework文件名称
9、Base SDK,
设置SDK支持平台
10、Dead Code Stripping
设置为YES。这时会过滤掉”dead”、”unreachable”的代码,即不会执行到的代码
11、Debug Information Level
设置为Line tables only。
这时调试信息允许获得带有函数名、文件名和行号的函数调用栈,
但是不包含其他数据(比如局部变量和函数参数),即断点依然会中断,但是无法在调试器中查看局部变量的值。
12、Link With Standard Libraries 设置为NO。
这时会避免重复链接。(不过有可能造成链接库找不到而报错)
13、Strip Style
设置为Non-Global Symbols。(Strip Linked Product为YES时Strip Style才生效;
对于库而言,最高去除符号的级别为Non-Global Symbols,如果为All Symbols则无法找到符号,从而引发报错)
14、Strip Linked Product
设置为YES,此时运行APP,断点不会中断,
在程序中打印 [NSThread callStackSymbols]也无法看到类名和方法名。
而在程序崩溃时,函数调用栈中也无法看到类名和方法名;
当该项为NO时,包的体积会变大,
因为它容纳了本要去除掉的调试信息。
<<>>
15、Strip Debug Symbols During Copy
设置为YES;与Strip Linked Product类似,
但是这个是将那些拷贝进项目包的第三方库、资源或者Extension的Debug Symbol去除掉,同样也是使用的strip命令。
这个选项没有前置条件,所以我们只需要在Release模式下开启,不然就不能对第三方库进行断点调试和符号化了。
<<<如果依赖的Target是独立签名的(比如App Extension),
strip操作就会失效,并伴随着Warning:warning: skipping copy phase strip, binary is code signed: xxxx。
此情况将依赖的Target 中的Strip Linked Product修改为YES,
保证依赖的Target是已经去除了符号即可,
Waning忽略掉就可以了>>>
16、Strip Swift Symbols
设置为YES;
此时移除相应Target中的所有的Swift符号,这个选项是默认打开的。
<<>>
17、Generate Debug Symbols
设置为NO来禁用Debug符号生成;
当值为YES时,APP crash会跳进framework源码,泄露了framework所有源代码,很不安全。
<<<当为YES时,可以通过Level of Debug Symbols来控制生成符号的级别>>>
18、Debug Information Format
设置为DWARF。这一项是设置是否将调试信息加入到可执行文件中。
改为DWARF后,如果程序崩溃,将无法输出崩溃位置对应的函数堆栈,但由于Debug模式下可以在XCode中查看调试信息,所以改为DWARF影响并不大。
不过,既然这个设置叫做Debug Information Format,所以首先得有调试信息。
如果此时Generate Debug Symbols选择的是NO的话,是没法产出dSYM文件的。
dSYM文件的生成,是在Strip等命令执行之前。
所以无论Strip Linked Product是否开启,生成的dSYM文件都不会受影响。
注意,静态库是无法生成dSYM文件的,即使设置为DWARF with dSYM File,构建过程中依然不会有生成dSYM文件的步骤。
<<<需要注意的是,将Debug Information Format改为DWARF之后,
会导致在Debug窗口无法查看相关类类型的成员变量的值。
当需要查看这些值时,可以将Debug Information Format改回DWARF with dSYM file,
clean(必须)之后重新编译即可>>>
提示:
--- Level of Debug Symbols有3个值:
used: 只引用符号
full: 所有符号
default: 使用编译器默认
--- Strip Style 表示的是我们需要去除的符号的类型的选项,其分为三个选择项:
All Symbols: 去除所有符号,一般是在主工程中开启。
Non-Global Symbols:去除一些非全局的Symbol
(保留全局符号,Debug Symbols同样会被去除),
链接时会被重定向的那些符号不会被去除,
此选项是静态库/动态库的建议选项。
Debug Symbols:去除调试符号,去除之后将无法断点调试
--- iOS的调试符号是DWARF格式,相关概念如下:
Mach-O: 可执行文件,源文件编译链接的结果。
包含映射调试信息(对象文件)具体存储位置的Debug Map。
DWARF:一种通用的调试文件格式,支持源码级别的调试,
调试信息存在于对象文件中,一般都比较大。
Xcode调试模式下一般都是使用DWARF来进行符号化的。
dSYM:独立的符号表文件,主要用来做发布产品的崩溃符号化。
dSYM 是一个压缩包,里面包含了DWARF文件。
使用Xcode编译打包的时候会先通过可执行文件的Debug Map获取到所有对象文件的位置,
然后使用dsymutil来将对象文件中的DWARF提取出来生成dSYM文件
参考文章:使用workspace搭建SDK开发框架