開發中咱們會使用到第三方的SDK,有的時候也會將整個系統的公用的功能的抽象出來成爲FrameWork,咱們只須要暴露對外的接口,使用者只須要調用接口,對於內部實現的過程不須要維護,能夠以庫的形式進行封裝,只暴露出頭文件。庫(FrameWork)是編譯好的二進制文件,編譯的時候只須要 Link 一下,提升浪費編譯時間,庫分爲靜態庫和動態庫。ios
靜態庫即靜態連接庫(Windows 下的 .lib,Linux 和 Mac 下的 .a)。之因此叫作靜態,是由於靜態庫在編譯的時候會被直接拷貝一份,複製到目標程序裏,這段代碼在目標程序裏就不會再改變了。靜態庫的好處很明顯,編譯完成以後,庫文件實際上就沒有做用了。目標程序沒有外部依賴,直接就能夠運行。固然其缺點也很明顯,就是會使用目標程序的體積增大。xcode
動態庫動態庫即動態連接庫(Windows 下的 .dll,Linux 下的 .so,Mac 下的 .dylib)。與靜態庫相反,動態庫在編譯時並不會被拷貝到目標程序中,目標程序中只會存儲指向動態庫的引用。等到程序運行時,動態庫纔會被真正加載進來。動態庫的優勢是,不須要拷貝到目標程序中,不會影響目標程序的體積,並且同一份庫能夠被多個程序使用(由於這個緣由,動態庫也被稱做共享庫)。同時,編譯時才載入的特性,也可讓咱們隨時對庫進行替換,而不須要從新編譯代碼。動態庫帶來的問題主要是,動態載入會帶來一部分性能損失,使用動態庫也會使得程序依賴於外部環境。若是環境缺乏動態庫或者庫的版本不正確,就會致使程序沒法運行(Linux 下喜聞樂見的 lib not found 錯誤)。iphone
xCode6以後製做動態庫相比以前簡單不少,xCode7基本上沿襲了xCode6的操做,細節方面有差異。在 iOS 8 以前,iOS 平臺不支持使用動態 Framework,開發者可使用的 Framework 只有蘋果基礎的 UIKit.Framework,Foundation.Framework 等。iOS 8/Xcode 6 推出以後,iOS 平臺添加了動態庫的支持,同時 Xcode 6 也原生自帶了 Framework 支持(動態和靜態均可以),iOS8多了 Extension ,Extension 和 App 是兩個分開的可執行文件,同時須要共享代碼,這種狀況下動態庫的支持就是必不可少的了。可是這種動態 Framework 和系統的 UIKit.Framework 仍是有很大區別。系統的 Framework 不須要拷貝到目標程序中,咱們本身作出來的 Framework 哪怕是動態的,最後也仍是要拷貝到 App 中(App 和 Extension 的 Bundle 是共享的),所以蘋果又把這種 Framework 稱爲Embedded FrameWork.工具
1.新建動態庫File→New→Target→Cocoa Touch FrameWork:性能
2.項目名稱DynamicLibrary,同時咱們新建兩個測試文件FEUIImage,TestImage:測試
3.在Dynamic.h中導入頭文件:ui
#import <UIKit/UIKit.h> //! Project version number for DynamicLibrary. FOUNDATION_EXPORT double DynamicLibraryVersionNumber; //! Project version string for DynamicLibrary. FOUNDATION_EXPORT const unsigned char DynamicLibraryVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import <DynamicLibrary/PublicHeader.h> #import <DynamicLibrary/FEUIImage.h>
4.將FEUIImage和TestImage設置爲Public供外部訪問:this
5.cmb+b編譯項目,編輯成功以後將項目移動到項目中進行測試:spa
6.經過FEDevice項目找到編譯以後的DynamicLibrary.framwork文件調試
咱們將上面的DynamicLibrary.framework移動的其餘項目中是能夠直接使用的,可是運行的時候會出錯, 錯誤信息以下:
Undefined symbols for architecture x86_64: "_OBJC_CLASS_$_FEUIImage", referenced from: objc-class-ref in ViewController.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)
錯誤信息跟動態的指令集和目標項目的指令集版本有關係,咱們能夠簡單的瞭解下Achitectures和設備之間的關係,iPhone一直以來都是Arm處理器,Arm是處理器是移動設備上佔用率最大的處理器。 在iOS模擬器上運行的是x86指令集,只有在真機上纔會執行arm指令集,每一個指令集對應不一樣的機型設置:
都是arm處理器的指令集。一般指令是向下兼容的。在模擬器運行時,iOS模擬器運行的是x86指令集。只有在真機上,纔會對執行arm指令集。
armv6:iPhone,iPhone 2G/3G,iPod 1G/2G,xCode4.5已經不支持armv6指令集;
armv7 :iPhone 3GS,iPhone4,iPhone 4s,iPad,iPad2,iPad3(The New iPad),iPad mini,iPod Touch 3G,iPod Touch4,因爲iPhone4s的佔有率,目前是指令集的最低版本;
armv7s:iPhone5, iPhone5C,iPad4和iPod5;
arm64:iPhone5s,iPhone6,iPhone6 Plus,iPad Air,iPad mini2(iPad mini with Retina Display),iPhone6s,iPhone6s Plus
咱們能夠經過lipo命令查看發現i386是mac版指令集:
lipo -info DynamicLibrary.framework/DynamicLibrary Non-fat file: DynamicLibrary.framework/DynamicLibrary is architecture: i386
1.若是用真機調試的話,一樣會發生程序錯誤,因此須要將一樣的代碼同時支持模擬器和真機,兩份庫聚合一下,回到DynamicLibrary.frameWork項目,經過File→New→Target→Other→Aggregate:
2.執行編譯腳本地址,先選中DynamicLibrary-Universal,添加腳本地址:
/${PROJECT_DIR}/DynamicLibrary/ios-framework-universal-script.sh
3.設置腳本內容,同時設置DynamicLibrary-Universal的依賴項(Target Dependencies)爲DynamicLibrary:
#!/bin/sh # ios-build-framework-script.sh # DynamicLibrary 博客園-FlyElephant # 博客園:http://www.cnblogs.com/xiaofeixiang/ # Created by keso on 16/1/17. # Copyright © 2016年 FlyElephant. All rights reserved. set -e set +u ### Avoid recursively calling this script. if [[ $UF_MASTER_SCRIPT_RUNNING ]] then exit 0 fi set -u export UF_MASTER_SCRIPT_RUNNING=1 ### Constants. UF_TARGET_NAME=${PROJECT_NAME} FRAMEWORK_VERSION="A" UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal IPHONE_DEVICE_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos IPHONE_SIMULATOR_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator ### Functions ## List files in the specified directory, storing to the specified array. # # @param $1 The path to list # @param $2 The name of the array to fill # ## list_files () { filelist=$(ls "$1") while read line do eval "$2[\${#$2[*]}]=\"\$line\"" done <<< "$filelist" } ### Take build target. if [[ "$SDK_NAME" =~ ([A-Za-z]+) ]] then SF_SDK_PLATFORM=${BASH_REMATCH[1]} # "iphoneos" or "iphonesimulator". else echo "Could not find platform name from SDK_NAME: $SDK_NAME" exit 1 fi ### Build simulator platform. (i386, x86_64) echo "========== Build Simulator Platform ==========" echo "===== Build Simulator Platform: i386 =====" xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_SIMULATOR_BUILD_DIR}/i386" SYMROOT="${SYMROOT}" ARCHS='i386' VALID_ARCHS='i386' $ACTION echo "===== Build Simulator Platform: x86_64 =====" xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_SIMULATOR_BUILD_DIR}/x86_64" SYMROOT="${SYMROOT}" ARCHS='x86_64' VALID_ARCHS='x86_64' $ACTION ### Build device platform. (armv7, arm64) echo "========== Build Device Platform ==========" echo "===== Build Device Platform: armv7 =====" xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_DEVICE_BUILD_DIR}/armv7" SYMROOT="${SYMROOT}" ARCHS='armv7 armv7s' VALID_ARCHS='armv7 armv7s' $ACTION echo "===== Build Device Platform: arm64 =====" xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_DEVICE_BUILD_DIR}/arm64" SYMROOT="${SYMROOT}" ARCHS='arm64' VALID_ARCHS='arm64' $ACTION ### Build device platform. (arm64, armv7) echo "========== Build Universal Platform ==========" ## Copy the framework structure to the universal folder (clean it first). rm -rf "${UNIVERSAL_OUTPUTFOLDER}" mkdir -p "${UNIVERSAL_OUTPUTFOLDER}" ## Copy the last product files of xcodebuild command. cp -R "${IPHONE_DEVICE_BUILD_DIR}/arm64/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" ### Smash them together to combine all architectures. lipo -create "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/i386/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/x86_64/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/armv7/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/arm64/${PROJECT_NAME}.framework/${PROJECT_NAME}" -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" ### Create standard structure for framework. # # If we don't have "Info.plist -> Versions/Current/Resources/Info.plist", we may get error when use this framework. # # MyFramework.framework # |-- MyFramework -> Versions/Current/MyFramework # |-- Headers -> Versions/Current/Headers # |-- Resources -> Versions/Current/Resources # |-- Info.plist -> Versions/Current/Resources/Info.plist # `-- Versions # |-- A # | |-- MyFramework # | |-- Headers # | | `-- MyFramework.h # | `-- Resources # | |-- Info.plist # | |-- MyViewController.nib # | `-- en.lproj # | `-- InfoPlist.strings # `-- Current -> A # echo "========== Create Standard Structure ==========" mkdir -p "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Versions/${FRAMEWORK_VERSION}/" mv "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Versions/${FRAMEWORK_VERSION}/" mv "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Headers" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Versions/${FRAMEWORK_VERSION}/" mkdir -p "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Resources" declare -a UF_FILE_LIST list_files "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/" UF_FILE_LIST for file_name in "${UF_FILE_LIST[@]}" do if [[ "${file_name}" == "Info.plist" ]] || [[ "${file_name}" =~ .*\.lproj$ ]] then mv "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${file_name}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Resources/" fi done mv "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Resources" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Versions/${FRAMEWORK_VERSION}/" ln -sfh "Versions/Current/Resources/Info.plist" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Info.plist" ln -sfh "${FRAMEWORK_VERSION}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Versions/Current" ln -sfh "Versions/Current/${PROJECT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" ln -sfh "Versions/Current/Headers" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Headers" ln -sfh "Versions/Current/Resources" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Resources" ### Open the universal folder. open "${UNIVERSAL_OUTPUTFOLDER}"
上面的腳本內容在腳本執行的過程當中,會依次編譯出支持 i38六、x86_6四、arm6四、armv七、armv7s 的包,而後把各個包中的庫文件經過 lipo 工具合併爲一個支持各平臺的通用庫文件,再基於最後一個 xcodebuild 命令打出的包的結構(arm64/DynamicLibrary.framework)和這個通用庫文件生成一個支持各個平臺的通用 Framwork;最後編譯以後會彈出framework的位置:
4.設置了通用庫以後,還須要在Genral下Embedded Binaries添加一下動態庫:
動態 Framework 是開發中優先考慮的代碼打包方式,可是爲了兼容一些低版本系統對動態庫的限制,咱們須要打包靜態庫來使用,實現起來比較簡單,在原有的DynamicLibrary項目 Build Settings 下設置 Mach-O Type 值爲 Static Library,能夠編譯出靜態庫。
設置爲靜態庫以後動態庫的最後一步Embedded Binaries就不用再添加了,若是已經添加建議刪除~