转载请注明出处:
http://www.jianshu.com/p/04a7e28f11b9
作者:纪小衰
framework的使用十分方便,一般来说直接拖入到项目中就可以直接使用接口,而不用在意具体的实现细节,对于功能的封装是个很好的途径。当我们写的工具给别人使用的同时又不想让别人知道自己的代码,这个时候使用framework就可以派上用场了(ps:个人觉得开源是一个大牛必须要体会到的思想)。。。
废话不多说,xcode如今已经到了7,对于framework的制作可以说是十分方便了。公司之前的framework的制作都是采用执行纯脚本的方法,可以直接命令行编译和融合不同版本(模拟器,真机,debug,release)的framework,个人不是很喜欢,还是喜欢用较为官方的方式来制作framework,当然为了方便还是少量地参照了网上的一些脚本。
一、新建framework工程
xcode->file->new->project选中iOS下的cocoa touch framework->next
接下来的步骤和创建一个普通的工程没什么区别,创建完毕以后在工程中新建JRModel类和JRModel2类文件,在我们的framework工程创建以后系统会默认创建一个和工程名相同的头文件,我这里是JRDataModel.h。一般来说我们会把framework中需要提供给外部的头文件都在这个头文件中import一下,这样使用者只要包含一个头文件就可以使用所有的类了。
这个地方需要注意到两点,一个就是导入的时候必须要使用framework的方式导入,使用<包名/头文件名.h>,因为在实际使用framework的时候是需要从包中搜索头文件的。还有一个坑就是右边的那个设置,对于总的头文件还有所有需要用到的头文件,都需要公开,选中以后把右边包的编译属性改为public,否则打出来的包在别的工程中实际上是不能引用的。
二、编译工程
接下来就是开始编译工程了(如下图),选中运行按钮右边的编译目标,选择Generic iOS Device,然后command+b编译一下,这个时候我们工程中的products文件夹下面的framework文件会由原来的红色(表示文件丢失)变成黑色(表示文件存在)。如果我们选择的是Generic iOS Device那么编译出来的framework只能在真机中使用,如果选中的是普通的模拟器,那么编译出来framewo只能在模拟器中使用。
tips:这里需要注意一下,如果一开始选择的是模拟器,那么即使build成功,framework也不会变成黑色,可能是因为这里的framework指向的是真机中对应的framework,但是在相对应的目录下的模拟器对应的framework是存在的
右击生成好的framework,选中show in finder可以查看framework在磁盘中的位置。进入目录以后,一般来说最多有四个目录,我这里只有三个(如下图)因为对于模拟器一般不会要release版本的。也可以通过finder,快捷键control+command+g输入~/Library/Developer/Xcode/DerivedData/Build/Products默认路径进入
分别在模拟器和真机选项中编译,发现实际只产生了模拟器和真机的debug版本的framework包,而没有release版本的包。这需要我们调整一下xcode的build类型,选中运行的target在下来列表中选Edit Scheme,进入以后把run中的Build Configureation修改为Release,这时候就可以编译出release版本的包了(如下图)
三、合并framework
到此,我们已经制作出对于各种情况下使用的framework了,对于不同的环境需要使用不同种类的framework,这在很多时候显得很麻烦。能不能制作一个能在所有场景使用的包呢。
这个时候我们可以考虑合并所有版本的包,一般来说可以使用脚本进行合并,但是每次使用脚本比较麻烦,我们可以在xcode中添加一个共同体aggregate,然后添加编译脚本来实现。
在工程中点击上方工具栏File->New->Target,在选项中选择Other中的Aggregate,命名最好和工程名有关,我这里就写成JRDataModelAggregate。
新建好以后,选择targets列表下的JRDataModelAggregate,点击build phases,点击+新建一个New Run Script Phase,把下面的脚本复制到shell下的选项中(脚本来源自网络,感谢这段脚本的原作者)。有能力的可以根据自己的需要修改下面的脚本。
# 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(上图左上角那个黄色的选项),在下拉的列表中选中我们刚才创建的JRDataModelAggregate,command+B编译一下,如果没什么意外,等到编译成功便会弹出我们编译好的framework了。
四、验证framework
framework其实可以看做是一个带扩展名的文件夹,所以我们可以直接进入framework的内部。通过下面的控制台指令进入到framework中(cd后面是你自己的framework路径)
cd /Users/ctzxh/Desktop/JRModel/JRDataModel/Products/JRDataModel.framework
执行下面的命令判断当前framework支持的架构
lipo -info JRDataModel
Tips1:
对于上面的每种处理器架构对应的设备请自行百度。这里说明一下,如果让你的包可以支持armv7s的架构(这里说的支持指的是针对这种架构的优化),可以根据下图的方式添加,新版的xcode默认便没有加上armv7s的架构,由于处理器向下兼容,所以即使不添加,所使用包的工程也是可以在armv7s架构上的机器运行的。个人推荐还是不必考虑了,按照官方的来就可以了。添加完以后在验证包的时候会多显示一个armv7s。
Tips2:
上面用Aggregate制作的库是拖入到使用的工程是直接可以使用的,但是之前分别制作的库直接拖入是不能使用的,解决的方式有两种:
第一种:制作链接库的时候,选择生成静态库
第二种:在使用动态链接库的时候嵌入二进制库,在下面的选项中添加要导入的动态库
网上有种方式是把framework的属性从required变成Optional,亲测行不通,能导入但是不能用。。。
就到这儿吧~~~
Add:如果只做的是.a的静态库可以使用下面的脚本,方法一样
if["${ACTION}"="build"]
then
#要build的target名
target_Name=${PROJECT_NAME}
echo"target_Name=${target_Name}"
#build之后的文件夹路径
build_DIR=${SRCROOT}/build
echo"build_DIR=${build_DIR}"
#真机build生成的头文件的文件夹路径
DEVICE_DIR_INCLUDE=${build_DIR}/Release-iphoneos/include/${PROJECT_NAME}
echo"DEVICE_DIR_INCLUDE=${DEVICE_DIR_INCLUDE}"
#真机build生成的.a文件路径
DEVICE_DIR_A=${build_DIR}/Release-iphoneos/lib${PROJECT_NAME}.a
echo"DEVICE_DIR_A=${DEVICE_DIR_A}"
#模拟器build生成的.a文件路径
SIMULATOR_DIR_A=${build_DIR}/Release-iphonesimulator/lib${PROJECT_NAME}.a
echo"SIMULATOR_DIR_A=${SIMULATOR_DIR_A}"
#目标文件夹路径
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}
echo"INSTALL_DIR=${INSTALL_DIR}"
#目标头文件文件夹路径
INSTALL_DIR_Headers=${SRCROOT}/Products/${PROJECT_NAME}/Headers
echo"INSTALL_DIR_Headers=${INSTALL_DIR_Headers}"
#目标.a路径
INSTALL_DIR_A=${SRCROOT}/Products/${PROJECT_NAME}/lib${PROJECT_NAME}.a
echo"INSTALL_DIR_A=${INSTALL_DIR_A}"
#判断build文件夹是否存在,存在则删除
if[ -d"${build_DIR}"]
then
rm -rf"${build_DIR}"
fi
#判断目标文件夹是否存在,存在则删除该文件夹
if[ -d"${INSTALL_DIR}"]
then
rm -rf"${INSTALL_DIR}"
fi
#创建目标文件夹
mkdir -p"${INSTALL_DIR}"
#build之前clean一下
xcodebuild -target${target_Name}clean
#模拟器build
xcodebuild -target${target_Name}-configuration Release -sdk iphonesimulator
#真机build
xcodebuild -target${target_Name}-configuration Release -sdk iphoneos
#复制头文件到目标文件夹
cp -R"${DEVICE_DIR_INCLUDE}""${INSTALL_DIR_Headers}"
#合成模拟器和真机.a包
lipo -create"${DEVICE_DIR_A}""${SIMULATOR_DIR_A}"-output"${INSTALL_DIR_A}"
#打开目标文件夹
open"${INSTALL_DIR}"
fi