一、为什么用库
1、为了方便共享源代码,在分享的同时又不愿意暴露源代码及具体实现
2、实现iOS程序组件化,可以把固定的业务模块化成库文件,方便多个工程使用,节省编译时间
3、开发第三方SDK的需要(企业级业务)
二、库的简介
2.1、什么是库?
库是一段编译好的二进制代码,附加上公开的头文件,代码就可以供其他开发者使用。在使用库的时候,编译时只需要链接就好。根据库的链接方式,分为静态库和动态库两类。
2.2、静态库与动态库的区别?
静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝。
动态库:链接时不复制到可执行文件中,程序运行时由系统动态加载到内存,程序中只存储指向动态库的引用。系统只加载一次,多个程序共用,节约了内存。
针对iOS项目,可以以下面的方式来简单区分:
以==Embedded Binaries==方式导入为动态库;
以==Link Binary With Libraries== 方式导入为静态库。
2.3、iOS中静态库
.a和.framework。
主流的SDK开发方式,腾讯、百度等大厂的SDK主要采用静态开发方式。
静态库不能嵌套(见iOS中Framework Library嵌套使用),这样可以减少库与其他库之间的耦合。
但是在使用静态库的时候,需要导入所依赖的其他库文件,这样会比较麻烦。因此,当SDK需要引用其他SDK,且不希望将内部实现暴露给用户时,那么就会采用动态库形式,这种一般是系统级的库文件。
2.4、iOS中动态库
.dylib,.tbd和.framework,主要是系统组件。
若SDK和项目中用到相同的三方库(如:AFNetworking),在使用动态库时,工程和项目中可以存在2份AFNetworking,因此开发相对方便一些。而静态库只会存在一份,因此开发相对复杂,但是可以减少代码的冗余。
注意:
在导入自定制的动态库时,要在Embedded Binaries中导入,不然会报错:image not found。
2.5、framework为什么既是静态库又是动态库?
.framework本质上并不是一个库,它是苹果为了方便开发者提供了一种库的打包方式,Framework会将Mach-O文件、头文件和资源包全都包含进来,不需要你再手动整理,我们也可以通过Xcode来制作framework动态库使用。
系统的.framework是动态库形式。
开发者所创建的.framework大部分是静态库,因此需要拷贝到目标程序中。
所以说.framework既可以是静态库也可以是动态库,这取决于编译成的Mach-O(就是那个二进制文件)是以动态库还是静态库形式编译。
综上: .framework是iOS开发中库的一种打包形式,既可以是动态库也是静态库。
2.6、a与.framework有什么区别?
.a是一个纯二进制文件,.framework中除了有二进制文件外还有资源文件,一个比较简单的描述即.a + .h + sourceFile = .framework。
所以.a文件不能直接使用,需要添加对应的.h文件,而.framework文件可以直接供开发者使用。
2.7、静态库开发的注意事项:
1、无论是.a静态库还.framework静态库,需要的都是二进制文件+.h+其它资源文件的形式。不同的是,.a本身就是二进制文件,需要我们手动加上.h和其它资源文件,而.framework本身已经包含了.h和其它文件。
2、图片资源的处理:在.a和.framewok两种静态库文件,一般都是把图片资源拿出,相应放在一个.bundle、.bundle的名字可以和.a或.framework的名字相同。一种方式是:可直接创建一个文件夹,后缀名为.bundle,在显示包内容时,添加图片资源文件。另一种方式是:在Xcode的工程中添加bundle target,添加图片资源文件。
3、Category在实际开发项目中会经常用到的。将Category打包成静态库,不会有什么问题的。但是在应用该静态库的工程中,会遇到调用Category的方法,找不到该方法的运行时错误(Selector not recognized)。这是因为链接标志所造成的,需要在应用静态库的工程配置,在==Other linker flags==处,添加『-ObjC』。
4、如果一个静态库较为复杂,必须暴露的.h文件过多,可在静态库的内部创建一个专门的.h文件(一般这个.h文件的名字和静态库名相同),然后把所有需要暴露的.h文件集中到此.h文件,再把这个.h文件暴露即可。
三、静态SDK开发流程
3.1、静态SDK的创建和配置
1、创建空白Xcode Workspace
2、创建SDK工程
(1)创建SDK
在当前workspace中创建SDK工程,如图1所示,并将SDK工程与workspace关联,如图2所示。
配置SDK为静态库。
配置==Deployment Target==iOS 9.0
Mach-O Type ==Static Library==
Enable Bitcode ==No==
Edit Scheme - Run - Info - Build Configuration ==Release==
(2)创建Bundle
因为在静态库中,SDK中的图片资源及xib等,需要添加到对应的Bundle中。所以在步骤(1)中的工程添加『target』,如图3所示,并配置如下:
Build Settings - Base SDK - ==iOS==
Deployment Target -9.0
Enable Bitcode ==NO==
清除Build settings - Installation Directory路径信息
COMBINE_HIDPI_IMAGES设置为==NO==
Edit Scheme - Run - Info - Build Configuration ==Release==
(3)配置生成脚本
在iOS开发中,模拟器运行平台为PC,架构为i386和x86_64等,而真机的架构为arm7和arm64等。所以,为了使SDK在模拟器及真机上都可运行,则需编译生成两个在不同平台的SDK,然后再用工具合二为一。
在SDK工程中,添加对应的Aggregate工程。
在设置Build Phrase中设置Target Dependices关联SDK工程,另外为Run Script添加下列代码,如图5所示。
# This is a modified script and it will ask the target to build separate libraries for simulator and device etc
# On project folder it will be found under Generated-Frameworks folder
# As per the documentations for XCode 10 Apple does not allow building FAT librares
# Go ahead and check https://forums.developer.apple.com/thread/66978 and https://forums.developer.apple.com/thread/109583
# Enjoy NicoX :)
set -e
# If we're already inside this script then die
if [ -n "$MULTIPLATFORM_BUILD_IN_PROGRESS" ]; then
exit 0
fi
export MULTIPLATFORM_BUILD_IN_PROGRESS=1
############################################
# Options
############################################
REVEAL_ARCHIVE_IN_FINDER=true
OUTPUT_DIR_NAME="Generated-Frameworks"
FRAMEWORK_NAME="${PROJECT_NAME}"
OUTPUT_DIR="${PROJECT_DIR}/${OUTPUT_DIR_NAME}/${FRAMEWORK_NAME}-${CONFIGURATION}-framework"
SIMULATOR_LIBRARY_OUT_DIR="${OUTPUT_DIR}/Simulator/"
GENERATED_LIBRARY_DIR="${BUILD_DIR}/${CONFIGURATION}-iphoneuniversal"
SIMULATOR_FRAMEWORK_OUT_DIR="${GENERATED_LIBRARY_DIR}/Simulator"
FRAMEWORK="${SIMULATOR_FRAMEWORK_OUT_DIR}/${FRAMEWORK_NAME}.framework"
########################################################################
# Build Frameworks
########################################################################
xcodebuild -scheme ${PROJECT_NAME} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator OBJROOT="${OBJROOT}/DependantBuilds"
########################################################################
# Create directory for general
########################################################################
rm -rf "${GENERATED_LIBRARY_DIR}"
mkdir "${GENERATED_LIBRARY_DIR}"
mkdir "${SIMULATOR_FRAMEWORK_OUT_DIR}"
mkdir "${FRAMEWORK}"
########################################################################
# Copy files Framework
########################################################################
SIMULATOR_LIBRARY_PATH="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${FRAMEWORK_NAME}.framework"
########################################################################
# Make a binary for simulaotr ie. x86_64 ot i386 file system
########################################################################
# For Swift framework, Swiftmodule needs to be copied in the universal framework
if [ -d "${SIMULATOR_LIBRARY_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule/" ]; then
cp -f "${SIMULATOR_LIBRARY_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule/"* "${FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/" | echo
fi
cp -r "${SIMULATOR_LIBRARY_PATH}/." "${FRAMEWORK}"
########################################################################
# Copy simulator build library in output folder
########################################################################
rm -rf "$OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR"
mkdir -p ${SIMULATOR_LIBRARY_OUT_DIR}
cp -r "${FRAMEWORK}" "$SIMULATOR_LIBRARY_OUT_DIR"
#########################################################################
echo "Simulator Lib Path ---->" "${SIMULATOR_LIBRARY_PATH}" "<----"
##########################################################################
# Now lets build for device; shall we?
##########################################################################
DEVICE_LIBRARY_OUT_DIR="${OUTPUT_DIR}/Device/"
DEVICE_FRAMEWORK_OUT_DIR="${GENERATED_LIBRARY_DIR}/Device"
FRAMEWORK="${DEVICE_FRAMEWORK_OUT_DIR}/${FRAMEWORK_NAME}.framework"
########################################################################
# Build Device Frameworks
########################################################################
xcodebuild -scheme ${PROJECT_NAME} -sdk iphoneos ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos OBJROOT="${OBJROOT}/DependantBuilds"
########################################################################
# Create directory for general
########################################################################
rm -rf "${GENERATED_LIBRARY_DIR}"
mkdir "${GENERATED_LIBRARY_DIR}"
mkdir "${DEVICE_FRAMEWORK_OUT_DIR}"
mkdir "${FRAMEWORK}"
########################################################################
# Copy files Framework
########################################################################
DEVICE_LIBRARY_PATH="${BUILD_DIR}/${CONFIGURATION}-iphoneos/${FRAMEWORK_NAME}.framework"
########################################################################
# Make a binary for device ie. arm7, arm7v, arm8 file system
########################################################################
# For Swift framework, Swiftmodule needs to be copied in the universal framework
if [ -d "${DEVICE_LIBRARY_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule/" ]; then
cp -f "${DEVICE_LIBRARY_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule/"* "${FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/" | echo
fi
cp -r "${DEVICE_LIBRARY_PATH}/." "${FRAMEWORK}"
########################################################################
# Copy device build library in output folder
########################################################################
mkdir -p ${DEVICE_LIBRARY_OUT_DIR}
cp -r "${FRAMEWORK}" "$DEVICE_LIBRARY_OUT_DIR"
echo "Device Lib Path ---->" "${DEVICE_LIBRARY_PATH}" "<----"
# Copy device build library in output folder
# Create Cross Platform Output
########################################################################
cp -r "${DEVICE_LIBRARY_OUT_DIR}/" "${OUTPUT_DIR}/"
lipo -create "${DEVICE_LIBRARY_OUT_DIR}${PROJECT_NAME}.framework/${PROJECT_NAME}" "${SIMULATOR_LIBRARY_OUT_DIR}${PROJECT_NAME}.framework/${PROJECT_NAME}" -output "${OUTPUT_DIR}/${PROJECT_NAME}.framework/${PROJECT_NAME}"
if [ ${REVEAL_ARCHIVE_IN_FINDER} = true ]; then
open "${OUTPUT_DIR}//"
fi
同上面的步骤,修改工程中的Target配置
Enable Bitcode 设置为==NO==
Edit Scheme - Run - Info - Build Configuration ==Release==
3.2、Demo工程的创建与配置
1、创建Demo工程
创建一个空白iOS工程,关联到当前workspace。
2、关联SDK工程
在Demo工程设置==Link Binary With Libraries==,加入SDK工程编译生成的framework,如图6所示。
3.3、SDK开发
1、SDK功能开发
在DemoSDK中添加源码文件,开发对应的功能。本例中添加一个简单的VC作为示例,
添加测试用的图片资源,
将新添加的代码头文件添加到SDK的Public Header中,
在SDK头文件中,引用新建的源码头文件
将SDK中Building Phases中的Copy Bundle Resources中的图片资源或者xib等移除掉,后续会加入到SDK专属的Bundle文件中。
在修改SDK工程的源码之后,清理并重新编译工程。
2、SDK Bundle生成
在SDK中的Bundle Target中,Copy Bundle Resources处添加对应的资源,
编译Bundle Target,在SDK工程目录中的Products目录下找到相应的.bundle文件,将其复制到SDK Demo工程所对应的文件中。然后在SDK Demo中的Copy Bundle Resouces里面添加刚才的.bundle文件。
3.3、SDK调试
此时在SDK Demo工程当中,引用SDK 头文件,测试SDK提供的功能和视图。
在模拟器中Run工程,在SDK源码中添加断点,然后在模拟器中查看实际效果。
四、手动应用SDK
4.1 生成Framework
选择SDK工程的Cross Target,编译运行Target,执行配置好的生成脚本,就打包好可以在模拟器与真机上都可成功运行的.framework文件。
4.2 使用SDK
类似于步骤3.2,将SDK的文件添加到目标工程中。
4.3 开发工程
参考
一、包含 Bundle 资源的 framework 的正确打包方式
四、Xcode中的 workspace, project, target, scheme