一、庫
庫是共享程序代碼的方式,一般分為靜態庫和動態庫。
二、靜態庫和動態庫
靜態庫形式:.a和.framework
靜態庫:鏈接時完整地拷貝至可執行文件中,被多次使用就有多份冗余拷貝。
動態庫形式:.dylib和.framework
動態庫:鏈接時不復制,程序運行時由系統動態加載到內存,供程序調用,系統只加載一次,多個程序共用,節省內存。
三、庫的創建
新建工程,然后如下所示來創建
.a靜態庫比較簡單,下邊的討論都是基于.Framework來說
Framework默認是動態庫,修改builsetting里的Mach-O Type可以修改成靜態庫
都是Framework怎么看是靜態庫還是動態庫呢?
動態庫里的二進制文件是個unix可執行文件,靜態庫不是,目前我是這么看的,如果有問題歡迎提出來。
注:系統提供的Framework都是動態庫
四、靜態庫制作
我們可以選擇編譯debug或者release版本的,如下圖所示,最后給外部使用的時候要用release版本,因為release版本做過編譯上的優化。
默認release版本build setting的build Active Architecture Only為No,可以生成所有架構
真機:arm64 armv7 armv7s
模擬器:i386 、x86_64
查看架構可以用命令?lipo -info StaticLibDemo
但真機和模擬器的庫是分開的,需要進行合并
命令行進入Products目錄執行如下命令
lipo -create Release-iphoneos/StaticLibDemo.framework/StaticLibDemo Release-iphonesimulator/StaticLibDemo.framework/StaticLibDemo -output?StaticLibDemo
就會在Products目錄生成合并后的二進制文件StaticLibDemo,然后復制到任意一個Framwork下替換對應的二進制文件,就是最后我們需要的Framwork。
當然也可以通過在xcode添加腳本在編譯時直接生成,在工程的Build Phases里添加以下腳本,真機和模擬器都Build一遍之后就會在工程目錄下生成Products文件夾,里面就是合并之后的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}/"
lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
fi
Framework資源文件
? 如果有資源文件,可以把放到bundle文件里,新建一個文件夾,后綴改成.bundle,然后把資源文件加入bundle文件就可以了。
?想把bundle文件打進Framwork里需要在Build Phases ->Copy Bundle Resources里添加bundle文件
?
靜態庫使用注意事項
如果靜態庫中有category類,則在使用靜態庫的項目配置中【Other Linker Flags】需要添加參數【-ObjC]或者【-all_load】
五、動態庫制作
動態庫制作方式跟靜態庫一樣,只要把Mach-O Type設置為Dynamic Library就行
動態庫使用
使用動態庫需要把動態庫添加到 General->Embedded Binaried下邊,不然會報錯
最后打出來的app除了主應用,還會有個Framworks的目錄,里邊是所有的動態庫
為了對比我們看下引用靜態庫后打出來的app,靜態庫全部打到主應用里了,并沒有Framework的目錄
在iOS8中App Store對上線的應用text代碼段是有限制的,我們公司的應用就超過了限制,但可以把一些庫打進動態庫,這樣就符合要求了,我們內部把所有的庫都做成pod了,這樣我們只需要新建一個動態庫target,把所有需要動態化的庫引入這個target就可以了,不管引入的是靜態庫還是動態庫只要引入這個target就動態化了
podfile里的寫法
對外的提供的庫,要支持制作動態庫,需要注意的坑
?1、使用自己的資源:寫成 [NSBundle bundleForClass:?[self class]],這樣無論在主工程還是在 Framework 中的代碼,都能找到自己的歸宿
系統庫 Category 的代碼,不能用 self,因為這個時候 self 還是系統庫,使用下面描述的?bundleForClass 方法
2、使用別人的資源:比如使用 某個庫 中的資源,那么就寫成?[NSBundle bundleForClass:?[庫的xx類 class]]
? ?i.由于引用的是別人的資源,所以這個類最好找一個穩定的,最好永遠不會改名或者刪除的類
? ii.由于引用的是別人的資源,所以最好要寫防御代碼,以應付找不到資源的情況
? iii.引用別人的資源這種做法,本身也很詭異,所以能避免就避免吧,實在不能避免,讓“別人”給你提供一個引用資源的接口
3、UIImage 的 imageNamed: 方法只會在 mainBundle 中搜索,需要改成使用 imageNamed:inBundle:compatibleWithTraitCollection: 方法
? i.inBundle 傳什么參數,根據實際情況使用上述的?[self class] 或者?bundleForClass
? ii.這個方法從 iOS 8 才有,如果你的庫還需要支持 iOS 7,注意做好 if 保護
? iii.這個方法跟 imageNamed: 一樣,從 iOS 9 開始才線程安全
4、訪問主程序的信息(比如 Info.plist),仍然使用 mainBundle,比如獲取 App 版本號
[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]
參考鏈接