iOS開發-動態和靜態FrameWork

開發中咱們會使用到第三方的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就不用再添加了,若是已經添加建議刪除~

參考連接:https://insert.io/frameworkios8xcode6/

http://www.cnblogs.com/xiaofeixiang/

相關文章
相關標籤/搜索