库是程序代码的集合,是共享程序代码的一种方式
根据源代码的公开情况,库可以分为 2 种类型
公开源代码,能看到具体实现
比如 SDWebImage 、 AFNetworking
不公开源代码,是经过编译后的二进制文件,看不到具体实现
主要分为:静态库、动态库
静态库: .a 和 .framework
动态库: .dylib 和 .framework
静态库:链接时,静态库会被完整地复制到可执行文件中, 被多次使用就有多份冗余拷贝 (左图所示)
动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存 (右图所示)
需要注意的是:
项目中如果使用了自制的动态库,不能被上传到 AppStore
Framework的引用
引用Framework很简单:
1.把.framwork文件放到项目工程目录下。
2.打开目标工程,Target设置->Link Binary With Libraries->Add Others,在目中找到.framework文件,添加即可。添加完成之后项目目录的Framework目录下会增加新增的Framework,可以在此处查看Framework的外部头文件。
3.framework中文件的引用需要使用尖括号<>,例如#import <TestFrame/TestFrame.h>
4.如果Framwork中包含分类,需要在Build Settings/Other Linker Flags中加入:-ObjC
一、静态库和动态库的区别:
静态库和动态库是相对编译期和运行期的,静态库以 .a 和 .framework 形式存在,链接时,静态库会被完整地复制到可执行文件中,被多次使用就有多份冗余拷贝;动态库以 .dylib 和 .framework 形式存在,链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存,一般只能由系统创建。
二、静态库简介:
1、静态库分为真机—Debug(调试)版本、真机—Release(发布)版本、模拟器—Debug版本、模拟器—Release版本;开发中一般都打包Release(发布)版本,将真机和模拟器版本合并,提供外界。
2、使用场景:在项目开发的过程中,例如两个公司之间业务交流,不可能把源代码都发送给另一个公司,这时候将私密内容打包成静态库,别人只能调用接口,而不能知道其中实现的细节。
三、用Xcode7创建静态库
.a文件版本(以制作AFNetworking静态库为例)
1、新建项目,点击iOS—Framework&—Cocoa Touch Static Library。
给你的工程命名为StaticLib
2、系统自动生成以工程名命名的.h和.m文件,可自定义的在目录下添加或删除文件,注意目录下Products文件夹有一个.a文件为红色,说明文件并不存在。这里我们将系统生成的.h和.m文件删除。
将AFNetworking的所有文件导入工程目录下
3、点击Build Phases—Copy Files,左下角点击+号按钮,添加你需要暴露的接口头文件。如果你在静态库工程中使用了category,那么你可能会碰到链接问题,解决的办法就是需要同时在生成静态库的工程和使用静态库的工程中使用“-all_load”编译选项,即在对应target的"Build Settings"中的“Other Linker Flags”选项添加“-all_load”,注意:使用静态库的工程中是一定要加该编译选项的!至于生成静态库的工程中加不加没有试过,不过建议还是加上该编译选项。
点击Add添加
4、然后点击左上角,选择Edit Scheme,Build Configuration下选择Release,先注意检查下面Release是否为NO:Yes表示只编译选中模拟器设备对应的架构,No则为编译所有模拟器设备支持的cup架构(Debug版本同理),选择NO,然后分别在模拟器和真机下Command+B编译一下,会看到Products文件夹下的.a文件变为黑色,这个.a文件就是我们想要得到的静态库。
注:关于静态库对CPU架构的支持,首先了解iOS设备CPU架构方面的知识,ARM是微处理器行业的一家知名企业,arm处理器以体积小和高性能的优势在嵌入式设备中广泛使用,几乎所有手机都是使用它的。
模拟器:iphone4s~5 : i386 iphone5s~6plus : x86_64
真机:iphone3gs~4s : armv7 iphone5~5c : armv7s (静态库只要支持了armv7,就可以跑在armv7s的架构上) iphone5s~6plus : arm64
armv6, armv7, armv7s是ARM CPU的不同指令集,原则是向下兼容的。例如iPhone4S CPU支持armv7, 但它同时兼容armv6,只是使用armv6指令可能无法充分发挥它的特性。
这里再补充一下查看静态库.a对处理器架构的支持,先cd到.a文件的路径下,命令行输入:lipo -info xxxxx.a
5、通过终端打开路径/Users/shelin/Library/Developer/Xcode/DerivedData/,选择对应的工程文件夹。
打开Build—Products文件夹,会看到Release-iphoneos和Release-iphonesimulator文件夹,分别是真机和模拟器的.a文件,为了使用方便我们将两个版本的.a文件合并。
6、合并真机和模拟器.a文件,在终端输入以下命令行:lipo -create 模拟器.a文件的路径 真机.a文件的路径 -output 合并后的保存路径(例:lipo -create /Users/shelin/Library/Developer/Xcode/DerivedData/StaticLib/Build/Products/Release-iphoneos/libxxx.a /Users/shelin/Library/Developer/Xcode/DerivedData/StaticLib/Build/Products/Release-iphonesimulator/libxxx.a -output /Users/shelin/Desktop/StaticLib.a)最终会在桌面得到一个合并后的StaticLib.a文件,再将暴露出来的.h头文件一起复制出来。
7、使用:只需将.a和暴露出来的.h头文件导入工程目录下就可供外界使用。
有没有写SDK或者要将一些常用的工具类做成Framework的经历? 你或许自己写脚本完成了这项工作,相信也有很多的人使用 iOS-Universal-Framework ,随着Xcode 6的发布,相信小伙伴们已经都知道了,Xcode 6支持做Framework了. 同时iOS-Universal-Framework开发者也宣布不在继续维持此项目的开发,建议开发者使用Xcode 6制作,目前网上也有很多制作iOS Framework的资料,但大多都不够详细,接下来本文会详情介绍一下在Xcode 6下制作iOS Framework.
关于静态库和动态库的概念,网上资料很多,这里不做叙述,只讲解制作过程。
创建iOS动态库
新建工程并选择默认Target为Cocoa Touch Framework, 如图:
做编码工作,在这里我简单的写了一个Utils的类,并写了一个log方法
设置开放的头文件:Framework中有些类可能是一些私有的辅助工具,不需要使用者看到,在这里只需要把开放出去的类放到Public下, 如图
这样生成的Framework的Headers目录下也只能看到Public的头文件
编码完成之后,直接Run就能成功生成Framework文件了,选择 xCode->Window->Organizer->Projects->Your Project, 打开工程的Derived Data目录,这样就能找到生成的Framework文件了,如图
新建测试工程,使用生成的Framework
将Framework文件导入到测试工程,调用Framework中的代码
MyUtils *utils = [MyUtils new]; [utils log:@"didFinishLaunchingWithOptions"];
运行报错(Reason: Image Not Found)
为什么会这样的?因为我们做的是动态库,在使用的时候需要额外加一个步骤,要把Framework同时添加到‘Embedded Binaries’中
注意: 在XCode 6之前是没有这个选项的(我没发现),所以理论上XCode 5及之前的版本无法使用Xcode 6下生成的Framework动态库。
到这里,假定你整个过程都是使用的模拟器做的,那看上去会很顺利。这时候尝试将测试工程部署到真机上,问题来了
ld: warning: ignoring file /work/ios/MyFrameworkTest/MyFrameworkTest/MyFramework.framework/MyFramework, file was built for x86_64 which is not the architecture being linked (armv7): /work/ios/MyFrameworkTest/MyFrameworkTest/MyFramework.framework/MyFramework
Undefined symbols for architecture armv7:
"_OBJC_CLASS_$_MyUtils", referenced from:
objc-class-ref in AppDelegate.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
为什么会这样?错误提示已经很明显了,因为我们制作动态库的时候,选的设备是模拟器,如果选真机的话,那生成的库也只能在真机上使用,那我们该怎样制作一个通用的动态库呢? 简单的方法是分别生成模拟器和真机上运行的库,然后在合并,这个方法,在每次生成动态库的时候,过程都会很繁琐,下面我们用一个脚本来自动完成它。
制作通用动态库
新建Aggregate Target
添加script到新建的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,Run, 如果没有异常的话,会自动弹出生成的Framework文件
这样生成的动态库就能同时支持模拟器和真机了。
Xcode 6下制作通用静态库
上面我们也提到了,这样生成的动态库恐怕很难在Xcode 5上使用,那我们为什么非要用动态库呢,一般情况下不是用静态库就好了吗? So Easy!只需要修改一个参数即可生成静态库了。
使用静态库的话,就可以把Framework从‘Embedded Binaries’中删除了. 亲测在Xcode 5下可用。把新生成的库导入到测试工程,试试在模拟器和真机上运行,一切OK.
不巧,如果你用的真机是iPhone5 C, 那悲剧又要发成了,生成的Framework竟然不支持armv7s,不知是Xcode 6的bug,还是因为苹果认为使用armv7s的设备太少,可以不支持了.Xcode 新建工程,默认的Architectures竟然不包含armv7s.
想要生成的库支持armv7s,把armv7s添加到Architectures中,重新生成Framework即可
判断一个Framework支持哪些架构
我们该怎么验证生成的Framework支持哪些平台呢,总不能一个个测试吧?当然不用.下面的命令是加上armv7s前后生成的framework的对比
Yearsdembp:Products Years$ lipo -info ./MyFramework.framework/MyFramework Architectures in the fat file: ./MyFramework.framework/MyFramework are: i386 x86_64 armv7 arm64 Yearsdembp:Products Years$ lipo -info ./MyFramework.framework/MyFramework Architectures in the fat file: ./MyFramework.framework/MyFramework are: armv7 armv7s i386 x86_64 arm64
iphone 静态库读取资源文件
在制作iphone静态库中并不能包含资源文件,虽然我们将资源文件(.png文件)拷贝到静态库工程中,但实际上这些.png是不会添加到target的,也就是说编译结果中并不包含这些资源,因此如果此时调用静态库,所有的资源(字符串、图片)都是缺失的。
我们可以把资源建立成单独的束(Bundle)。
新建工程“ Mac OS X -> Framework & Library -> Bundle ”,命名为:yhyLibraryBundle。
然后把上面.png文件拷进Resouces中去。编译,生成yhyLibraryBundle.bundle文件。
返回静态库工程,新建一个类:Utils 。
编辑Utils.h:
[pre]
[/pre]编辑Utils.m:
[pre]
[/pre]函数getMyBundlePath可以取得束yhyLibraryBundle中具体资源的绝对文件路径,如:
[pre]
[/pre]同时,修改CheckButton.m中的代码,导入Utils.h头文件,把其中获取图片的代码由imageNamed修改为imageWithContentsOfFile,如:
[pre]
[/pre]即通过绝对路径读取图片资源。
在运行生成.a文件之后不能通用模拟器和真机,通用的做法为:
可以使用命令行工具lipo将适用于真机与模拟器的静态库合二为一,操作如下
user#lipo -create /ospath/libname.a /simulatorpath/libname.a -output /allInOnelibName.a 即可
用如下命令可以看到合并后静态库支持的cpu架构信息
user#lipo -info /allInOnelibName.a
Architectures in the fat file: /Users/ipi/Desktop/libDemoLib_1.a are: armv7 i386
**这种做法缺点:通用静态库太大