系列:iOS开发-framework的制作与使用
作为一个iOS开发人员,无论是出于工作需要,还是出于保密需要...
我们都有可能或多或少的想要将一部分源码封装成静态或者动态库,以达到保密或者版权的效果
比如公司想要整理框架作为知识产权等销售,比如你做一个兼职工作,客户想要项目源码,涉及隐私你想要保护起来,比如你想要分享给一个同时一项功能,但是不想给源码...
网上关于Framework制作的教程数不胜数,但是大多都是比较老旧的,所以我把这几天自己制作framework的过程记录下来,并且使用的是最新的Xcode8环境。
前段日子我接了一份兼职,项目做完,完整上线,客户想要源码,出于保密,我想要把一部分主要的核心代码封装起来.
这个兼职本身并不复杂,也就几个页面,核心部分是一个蓝牙的封装和通讯部分
核心文件也就是这个蓝牙的实现,其他的话想对来说就是一些界面和自定义的一些简单控件,非核心,我打算封装一个framework来替换这部分代码
步骤:
于是我使用Xcode8创建.
选择Cocoa Touch Framework->Next
起一个略屌的名字并创建
把几个文件添加进入项目
接下来就是设置一些参数了
修改项目最低支持版本
修改项目是否支持bitcode
这个很多人封装都不一定会想起来并且设置,虽然没有大的影响,但是我觉得我还是保险起见.
接下来.
这里解释一下
1.首先是Dead Code Stripping设置为NO,网上对此项的解释如下,大致意思是如果开启此项就会对代码中的”dead”、”unreachable”的代码过滤,不过这个开关是否关闭,似乎没有多大影响,你可以选择忽视不设置
2.然后将Link With Standard Libraries关闭,关闭连接标准库,为了避免重复链接,当然这里还是可以忽视不设置
3.最后将Mach-O Type设为Static Library,framework可以是动态库也可以是静态库,对于系统的framework是动态库,而用户制作的framework只能是静态库。后面我会讲他们之间的区别
再继续设置
然后将需要公开的头文件从Project中拖入Public,至于是否需要将私有的头文件拖入Private,我觉得直接放在Project中即可,若是Private中有头文件,打包以后的framework中会多出一个Private的文件夹包含着放入Private的头文件,不过我觉得如果是私有最好还是不要让别人看到。
然后为了方便用户使用,修改XLXBLEManager.h的内容
这样初步的环境都配置好了
接下来我们就要封包了,封包的话我们需要考虑该库是否需要支持真机和模拟器同时进行?32位还是64位?debug还是release?
OK,这里是我的设置
我想要封装的是release的库
原因是
Release是发行版本,比Debug版本有一些优化,文件比Debug文件小 Debug是调试版本,Debug和Release调用两个不同的底层库。 1、"Debug是调试版本,包括的程序信息更多" 2、只有DEBUG版的程序才能设置断点、单步执行、使用TRACE/ASSERT等调试输出.3、我封装成库了,还需要断点?单步执行?...
选择release
接下来是支持的版本
Xcode默认不支持armv7s 我们可以添加上,当然也可以忽略
这里我更改了Build Active Architecture Only设置我NO,
如果选项设为YES,其编译时模拟器版本只会生成当前模拟器机器的框架,将其设置为NO后,发现用模拟器编译后生成的framework同时包含x86_64和i386架构。
当然这里有几点要说明,其默认的是DEBUG为YES,RELEASE为NO ,我刚刚修改了Scheme为release版本,所以这里其实默认就是NO,我还是可以不用设置
如果我没有修改Scheme,仍然是默认的debug,那么其就是默认的YES,那么结果是我们选择iphone5s及以上的模拟器,其框架为x86_64 iPhone5s以下的模拟器,其框架为i386,那样的话封装的库只能使用相对应的模拟器,否则就会崩溃报错,
为了避免这个问题,我选择NO,让其都支持,无论是debug还是release
OK,所有的准备工作基本全部做完
接下来是编译build
首先选择
我们用commond+B build一下->success
然后再随便选择一个模拟器,使用相同的方式再build一下->success
这样其实两个framework就生成了
在什么地方呢?
我们能够看到两个文件夹里面分别有一个framework
上面的是release版本的真机框架,下面的是release版本的模拟器框架,如果你选择的scheme是debug的话,则这两个文件夹是以debug开头,
这两个framework就是我想要的静态库,我们可以直接添加到想要的项目中使用,当然,我们会想要把两个库合并,让其能够在真机和模拟器同时运行
接下来就会用到终端命令行
打开终端,输入
lipo -create
然后把两个framework里面的XLXBLEManager分别拖到终端来
再后面再接着输入
-output ~/Desktop/framework/XLXBLEManager
完整的命令是这样的
表示的意思是,合并资源 a b 到指定文件夹 命名为 c 这里~/Desktop/framework/表示桌面的framwork的一个文件夹,你可以自定义
运行,我们在桌面的framework文件夹中发现了合并之后的文件
我们看一下其支持的版本
lipo -info XXX
各种版本都是支持的,是我想要的库,
我们进入刚才的Release-iphoneos文件夹复制一个XLXBLEManager.framework到桌面来
使用合成的XLXBLEManager替换掉XLXBLEManager.framework中的XLXBLEManager
这样,一个合成之后的完整的.framework就结束了
接下来是使用framework
在search path 中添加framework的Headers
将之前的代码引用更换成新的库的引用
运行!
这里封装的是静态的framework,那么动态framework有什么区别呢?
静态库: 链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝。
动态库: 链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。[ios暂时只允许使用系统动态库];
静态库和动态库是相对编译期和运行期的:静态库在程序编译时会被链接到目标代码中,程序运行时将不再需要改静态库;而动态库在程序编译时并不会被链接到目标代码中,只是在程序运行时才被载入,因为在程序运行期间还需要动态库的存在。
总结:同一个静态库在不同程序中使用时,每一个程序中都得导入一次,打包时也被打包进去,形成一个程序。而动态库在不同程序中,打包时并没有被打包进去,只在程序运行使用时,才链接载入(如系统的框架如UIKit、Foundation等),所以程序体积会小很多,但是苹果不让使用自己的动态库,否则审核就无法通过。
这里是我封装静态库的原因,
如果使用仍然想要使用动态库的话,那么在使用库的时候
还要选择在Embedded Binaries中添加相应的库
此外
在项目中引入静态库后,archive的时候如果出现bitcode错误
bitcode bundle could not be generated because ... was built without full bitcode.All object files and libraries for bitcode must be generated from Xcode Archive or Install build for architecture arm64
所以需要打开库工程的此选项并加上-fembed-bitcode参数,重新编译
如此archive时就不会出问题了,到此我在制作使用framework时遇到的问题都在这里了。
此外,还有引用框架编译的时候没有报错,在运行的时候报错了,是由于链接发生错误
在使用项目的tagert中的Other Linker Flags 中加入所需的参数,一般是这三个:-ObjC、-all_load、-force_load,尝试添加了第一个应该就能解决问题了
至此,制作framework的教程就基本结束了
但是上面的教程是否有点复杂了?
主要的点就在于制作framwork和合并framework上面,有没有办法能够傻瓜式,一键合成需要的静态库或者动态库呢?肯定有的
在选择好最低版本,bitcode设置成NO,Build Active Architecture Only设置我NO,将Mach-O Type设为Static Library之后,我们换一种方式
创建一个target
之后选择target
添加这段脚本
# Sets the target folders and the final framework product.
# 如果工程名称和Framework的Target名称不一样的话,要自定义FMKNAME
# 例如: FMK_NAME = "MyFramework"
FMK_NAME=${PROJECT_NAME}
# Install dir will be the final output to the framework.
# The following line create it in the root folder of the current project.
INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework
# Working dir will be deleted after the framework creation.
WRK_DIR=build
DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework
SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework
# -configuration ${CONFIGURATION}
# Clean and Building both architectures.
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos clean build
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator clean build
# Cleaning the oldest.
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.
lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/${FMK_NAME}"
rm -r "${WRK_DIR}"
open "${INSTALL_DIR}"
选择target之后运行
你会发现结果是直接在打包项目中添加了一个product文件夹并生成了需要的framework文件!
这个就是我们需要的framework文件,并且是合并之后的文件.
我们可以测试看看
省去了所有的build->合并->替换的手动过程
直接生成需要的framework文件,至于后面怎么使用framework文件,跟上面一样了...