为什么要使用动(静)态库?
**静态库的好处**:
1、模块化,分工合作
2、避免少量改动经常导致大量的重复编译连接
3、也可以重用,注意不是共享使用
**动态库使用有如下好处**:
1、使用动态库,可以将最终可执行文件体积缩小
2、使用动态库,多个应用程序共享内存中得同一份库文件,节省资源
3、使用动态库,可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的。
**结论: **将整个应用程序分模块,团队合作,进行分工,影响比较小等其他好处,
苹果iOS开发中其实是禁止使用动态库的,即使可以使用也是不纯正的动态库,是无法使用共享功能的.在 iOS 8 之前,iOS 平台不支持开发者使用用户自己的动态 Framework,appstore不能上架,因为 iOS 应用都是运行在沙盒当中,不同的程序之间不能共享代码。但是,iOS 8/Xcode 6 推出之后,因为Extension 和 App 是两个分开的可执行文件,同时需要共享代码,iOS添加了对动态库的支持.
动态库和静态库的区别?
1. 表现形式
静态库:.a和.framework; .a文件是一个纯二进制文件,.framework除了二进制文件还有外部资源文件;.a 文件不能直接使用,至少要有.h文件配合;.framework可以直接使用。
.a+.h+sourceFile = .framework。
动态库:.tbd(系统库)和.framework。
2. 编译链接静态库: 链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝。 动态库: 链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存
制作一个动态库
1.打开xcode工程
image2.设置参数在TARGETS下选中工程,在Build Settings下更改几个参数。
image image(1).Build Active Architecture Only: 指明是否只编译当前连接设备所支持的指令集,如果是,那么只编译出连接设备所对应的指令集,如果否,则编译出所有其它有效的指令集(由Architectures和Valid Architectures决定)。
(2).Dead Code Stripping, 设置为 NO 关闭对代码中“dead”,“unreachable”代码过滤.
(3). Link With Standard Libraries 设置为 NO 避免重复链接.
(4).mach -0 type ,即选择动态库(Dynamic Library)或静态库(Static Library)。
3.编写代码
image(1) 将头文件需要暴露出去的放入Public
(2) 测试获取bundle中的图片 (同framework, 动态库和静态库的bundle 路径有不同)
image(3) Embedded Binaries 这个要设置(必要的)
image4. 接下来就可以编译项目了
(1) 将编译好的framework 可以拖入项目中, 设置好对应的header search path 等
image(2) 导入以 <> 号形式import导入
5. 可能出现的问题
(1) Headers Not Found
如果Xcode找不到框架的头文件,你可能是忘记将它们声明为public了. 或者项目中search path 的设置问题
(2) 链接警告
第一次编译框架target时,Xcdoe会在链接阶段报告找不到文件夹:
ld: warning: directory not found for option'-L/Users/myself/Library/Developer/Xcode/DerivedData/MyFramework-ccahfoccjqiognaqraesrxdyqcne/Build/Products/Debug-iphoneos'
此时,可以clean并重新编译target,警告会消除。
(3) 制作的库支持的CPU指令集不全,所报的错误
image报错原因:
原来对方用模拟器测试运行的,其CPU架构为x86_64,我导入的framework是真机编译出来的动态库(支持的指令集为armv7、armv7s、arm64,并没有x86_64),所以报此错误,应该生成适合模拟器和真机的通用库。
(4) Core Data momd not found
对于框架项目和应用程序项目,Xcode会以不同的方式编译momd(托管对象模型文件)。Xcode会简单地在根目录创建.mom文件,而不会创建一个.momd目录(目录中包含VersionInfo.plist和.mom文件)。
这意味着,当从一个embedded framework的model中实例化NSManagedObjectModel时,你必需使用.mom扩展名作为model的URL,而不是采用.momd扩展名。
NSURL *modelURL = [[NSBundle mainBundle]URLForResource:@"MyModel" withExtension:@"mom"];
(5) Unknown class MyClass in Interface Builder file.
由于静态框架采用静态链接,linker会剔除所有它认为无用的代码。不幸的是,linker不会检查xib文件,因此如果类是在xib中引用,而没有在O-C代码中引用,linker将从最终的可执行文件中删除类。这是linker的问题,不是框架的问题(当你编译一个静态库时也会发生这个问题)。苹果内置框架不会发生这个问题,因为他们是运行时动态加载的,存在于iOS设备固件中的动态库是不可能被删除的。
有两个解决的办法:
让框架的最终用户关闭linker的优化选项,通过在他们的项目的Other Linker Flags中添加-ObjC和-all_load。
在框架的另一个类中加一个该类的代码引用。例如,假设你有个MyTextField类,被linker剔除了。假设你还有一个MyViewController,它在xib中使用了MyTextField,MyViewController并没有被剔除。你应该这样做:
在MyTextField中:
- (void)forceLinkerLoad_ {}
在MyViewController中:
+(void) initialize { [MyTextField forceLinkerLoad_]; }
他们仍然需要添加-ObjC到linker设置,但不需要强制all_load了。
第2种方法需要你多做一点工作,但却让最终用户避免在使用你的框架时关闭linker优化(关闭linker优化会导致object文件膨胀)。
(6) unexpected file type 'wrapper.cfbundle' in Frameworks &Libraries build phase
这个问题发生在把“假”框架项目作为workspace的依赖,或者把它当作子项目时(“真”框架项目没有这个问题)。尽管这种框架项目产生了正确的静态框架,但Xcode只能从项目文件中看出这是一个bundle,因此它在检查依赖性时发出一个警告,并在linker阶段跳过它。
你可以手动添加一个命令让linker在链接阶段能正确链接。在依赖你的静态框架的项目的OtherLinker Flags中加入:
-framework MyFramework
警告仍然存在, 但不会导致链接失败。
(7) Libraries being linked or not being linked into the finalframework
很不幸, “真”框架和“假”框架模板在处理引入的静态库/框架的工作方式不同的。
“真”框架模板采用正常的静态库生成步骤,不会链接其他静态库/框架到最终生产物中。
“假”框架模板采用“欺骗”Xcode的手段,让它认为是在编译一个可重定位格式的目标文件,在链接阶段就如同编译一个可执行文件,把所有的静态代码文件链接到最终生成物中(尽管不会检查是否确实目标代码)。为了实现象“真”框架一样的效果,你可以只包含库/框架的头文件到你的项目中,而不需要包含库/框架本身。
(8)Unrecognized selector in (some class with a category method)
如果你的静态库或静态框架包含了一个模块(只在类别代码中声明,没有类实现),linker会搞不清楚,并把代码从二进制文件中剔除。因为在最终生成的文件中没有这个方法,所以当调用这个类别中定义的方法时,会报一个“unrecognizedselector”异常。
要解决这个,在包含这个类别的模块代码中加一个“假的”类。linker发现存在完整的O-C类,会将类别代码链接到模块。
我写了一个头文件 LoadableCategory.h,以减轻这个工作量:
import "SomeConcreteClass+MyAdditions.h"
import "LoadableCategory.h" MAKE_CATEGORIES_LOADABLE(SomeConcreteClass_MyAdditions); @implementation SomeConcreteClass(MyAdditions)
...
@end
在使用这个框架时,仍然还需要在Build Setting的Other Linker Flags中加入-ObjC。
(9) 执行任何代码前单元测试崩溃
如果你在Xcode4.3中创建静态框架(或库)target时,勾选了“withunit tests”,当你试图运行单元测试时,它会崩溃:
Thread 1: EXC_BAD_ACCESS (code=2, address=0x0) 0 0x00000000 --- 15 dyldbootstrap:start(...)
这是lldb中的一个bug。你可以用GDB来运行单元测试。编辑scheme,选择Test,在Info标签中将调试器Debugger从LLDB改为GDB。
6. 脚本应用
(1) 合并framework
if [ "${ACTION}" = "build" ]
then
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework
DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework
SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
#ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"
# 使用lipo命令将其合并成一个通用framework
# 最后将生成的通用framework放置在工程根目录下新建的Products目录下
lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
#open "${DEVICE_DIR}"
#open "${SRCROOT}/Products"
fi
(2)瘦身命令样例
mkdir ./bak
cp -r MangoSDK.framework ./bak
lipo MangoSDK.framework/MangoSDK -thin armv7 -output MangoSDK_armv7
lipo MangoSDK.framework/MangoSDK -thin arm64 -output MangoSDK_arm64
lipo -create MangoSDK_armv7 MangoSDK_arm64 -output MangoSDK
mv MangoSDK MangoSDK.framework/
(3) 在打包时候只需要真机环境的脚本
在整个开发过程中,都不再碰到Framework引发的问题。可就在提交App Store时,Xcode报了这个错:
ERROR ITMS-90087: "Unsupported Architecture. Your executable contains unsupported architecture '[x86_64, i386]'."
按字面意思理解,是包里含有不支持的架构。x86_64和i386是模拟器架构,的确是在发布时不需要的。可是Framework是打好的包,如何去除是个问题
大意就是,读取目标app目录,取到Framework目录,遍历之,取出Framework,对其瘦身。
脚本添加后,可以指定在Archive时才使用,以免影响模拟器运行。
APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
EXTRACTED_ARCHS=()
for ARCH in $ARCHS
do
echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
done
echo "Merging extracted architectures: ${ARCHS}"
lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
rm "${EXTRACTED_ARCHS[@]}"
echo "Replacing original executable with thinned version"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
done
网友评论