iOS 靜態(tài)庫(Framework依賴、三方依賴、資源處理等)


目錄


一、 基本概念
     1. 靜態(tài)庫動態(tài)庫區(qū)別
     2. 庫的版本
     3. iOS 設(shè)備的CPU架構(gòu)
     4. ARM處理器指令集
     5. i386|x86_64 指令集
     6. Xcode中指令集相關(guān)選項
二、打包framewor靜態(tài)庫
     1. 創(chuàng)建項目-》創(chuàng)建靜態(tài)庫項目
     2. 靜態(tài)庫如何開發(fā)調(diào)試?
     3. 腳本合成靜態(tài)庫
     4. 自己打包的靜態(tài)庫依賴第三方.a和.framework靜態(tài)庫?
     5. 自己打包的靜態(tài)庫依賴第三方框架比如YYModel、AFN?
     6. 靜態(tài)庫依賴的圖片、xib等資源處理?
     7. -ObjC / -all_load / -force_load 什么鬼


文章中的demo資源代碼可以從這里下載

靜態(tài)庫和動態(tài)庫

  • 靜態(tài)庫 -> .a或者.framework的庫

  • 動態(tài)庫 -> .dylib或者.framework的庫

區(qū)別:

  • 靜態(tài)庫在系統(tǒng)中多次使用就有多份拷貝 (比如A應(yīng)用里有個xxx.a的靜態(tài)庫、B應(yīng)用里有一個xxx.a的靜態(tài)庫,那么在系統(tǒng)內(nèi)存里就會占用兩份)

  • 動態(tài)庫是由系統(tǒng)動態(tài)加載到內(nèi)存,而且只加載一次,多個程序共用節(jié)省內(nèi)存

我們自己制作的動態(tài)庫蘋果不允許上架到App Store 所以這里不做介紹 下面主要說一下靜態(tài)庫

庫的版本

Debug版本 -》 真機(jī)Debug版本 / 模擬器Debug版本

包含完整的符號信息方便調(diào)試
不會對代碼進(jìn)行優(yōu)化

Release版本 -》真機(jī)Release版本 / 模擬器Release版本

不包含完整的符號信息
執(zhí)行代碼進(jìn)行過優(yōu)化
執(zhí)行速度會更快些

幾個概念

iOS 設(shè)備的CPU架構(gòu)

  • 在模擬器上支持:

      iPhone4s-5:  i386 架構(gòu)
      iPhone5s-8 Plus: x86_64 架構(gòu)
    
  • 在真機(jī)設(shè)備上支持:

      armv6: iPhone、iPhone 2、iPhone 3G、iPod Touch(第一代)、iPod Touch(第二代)
      armv7: iPhone 3Gs、iPhone 4、iPhone 4s、iPad、iPad 2
      armv7s: iPhone 5、iPhone 5c (靜態(tài)庫只要支持了armv7,就可以在armv7s的架構(gòu)上運(yùn)行)
      arm64: iPhone 5s、iPhone 6、iPhone 6 Plus、iPhone 6s、iPhone 6s Plus、iPad Air、iPad Air2、iPad mini2、iPad mini3
    

ARM處理器指令集

  • 幾乎所有手機(jī)處理器都基于ARM處理器的,ARM處理器特點是體積小、低功耗、低成本、高性能,所以在嵌入式系統(tǒng)中應(yīng)用廣泛

      armv6|armv7|armv7s|arm64都是ARM處理器的指令集
      這些指令集都是向下兼容的
    

i386|x86_64 指令集:i386和x86_64 是Mac處理器的指令集

  • i386是針對intel通用微處理器32位處理器的
  • x86_64是針對x86架構(gòu)的64位處理器

所以當(dāng)使用iOS模擬器的時候會遇到i386|x86_64(ios模擬器沒有arm指令集)

模擬器32位處理器測試需要i386架構(gòu)。
模擬器64位處理器測試需要x86_64架構(gòu)。
真機(jī)32位處理器需要armv7或者armv7s架構(gòu)。
真機(jī)64位處理器需要arm64架構(gòu)

命令查看靜態(tài)庫支持的架構(gòu):

通過 lipo -info - 》 拖入模擬器或者真機(jī).framework的路徑 查看靜態(tài)庫支持的架構(gòu)

Xcode中指令集相關(guān)選項(Build Setting中)

1.png
1. Architectures

指定工程被編譯成支持哪些指令集類型.

支持的指令集越多,就會編譯出很多個指令集代碼的數(shù)據(jù)包,對應(yīng)生成二進(jìn)制包就越大,也就是ipa包越大.

2. Valid Architectures

限制可能被支持指令集的范圍.

xcode編譯出來的二進(jìn)制包類型最終從這些類型產(chǎn)生,而編譯出哪些指令集的包,將由Architectures與Valid Architectures這些交集來確定,面會舉例說明.

3. Build Active Architecture Only

指定只對當(dāng)前連接設(shè)備所支持的指令集編譯.

當(dāng)設(shè)置為YES時是為了debug編譯的速度更快,它只會編譯當(dāng)前的architecture版本

當(dāng)設(shè)置為NO時,會編譯所有的版本,所以一般debug設(shè)置為YES,release設(shè)置為NO以適應(yīng)不同設(shè)備.

配置參數(shù)舉例
  • 例1

      Valid Architectures設(shè)置支持arm指令集有:armv7/armv7s/arm64
      Architectures設(shè)置的支持arm指令集有:armv7s 
      這時Xcode只會生成一個armv7s指令集的二進(jìn)制包
    
  • 例2

      Valid Architectures支持:armv7s/arm64
      Architectures支持:armv7/armv7s
      這時Xcode生成二進(jìn)制包所支持的指令集:armv7s
    
  • 例3:

      ValidArchitectures支持: armv6/armv7s/arm64
      Architectures支持: armv7/armv7s/arm64
      Xcode生成生成二進(jìn)制包支持的指令集:arm64
    
  • 例4

      Valid Architectures: armv6/armv7s/arm64
      Architectures: armv6/armv7/armv7s
      Xcode生成二進(jìn)制包支持的指令集:armv7s
    
  • 例5:

      Valid Architectures: armv7/armv7s
      Architectures: armv7/armv7s/arm64
      這種情況是報錯的 因為允許使用指令集中沒有arm64
    

Xcode6.1.1里的Valid Architectures設(shè)置默認(rèn)為 Standard architectures(armv7,arm64),如果想改的話自己在other中更改

結(jié)論: 使用 standard architectures (including 64-bit)(armv7,arm64) 參數(shù),則打的包里面有32位、64位兩份代碼,在iPhone5s(iPhone5s的cpu是64位的)下,會首選運(yùn)行64位代碼包, 其余的iPhone(其余iPhone都是32位的,iPhone5c也是32位),只能運(yùn)行32位包,但是包含兩種架構(gòu)的代碼包,只有運(yùn)行在ios6以上的系統(tǒng)上

而使用 standard architectures (armv7,armv7s)參數(shù),則打的包里只有32位代碼,iPhone5s的cpu是64位,但是可以兼容32位代碼,即可以運(yùn)行32位代碼。但是這會降低iPhone5s的性能。 其余的iPhone對32位代碼包更沒問題, 而32位代碼包,對系統(tǒng)也幾乎也沒什么限制。

從需求開始

比如現(xiàn)有騰訊廣告的.a靜態(tài)庫 和 頭條的.framework靜態(tài)庫, 還有我們自己公司的廣告處理類, 現(xiàn)在要把這三家廣告業(yè)務(wù)集合在一起打包成一個framwork靜態(tài)庫。

帶著疑問開始

  • 我們要制作的framwork靜態(tài)庫 包含其他第三方的framwork靜態(tài)庫怎么辦?
  • 我們要制作的framwork靜態(tài)庫 依賴第三方框架 比如AFN、YYModel等怎么辦?
  • 我們要制作的framwork靜態(tài)庫 依賴的資源 比如圖片、XIB怎么辦?
  • 制作好的靜態(tài)庫怎么進(jìn)行調(diào)試?
  1. 創(chuàng)建靜態(tài)庫項目 ZZAdSDK
image.png

接著添加target ,選擇創(chuàng)建Cocoa Touch Framwork


image.png
image.png

之所有沒有直接選擇Cocoa Touch Framwork創(chuàng)建靜態(tài)庫,而是通過Target方式創(chuàng)建靜態(tài)庫,是因為涉及到靜態(tài)庫的調(diào)試問題。

比如我們制作好了一個靜態(tài)庫,然后把它拖入到項目里,然后發(fā)現(xiàn)有些問題,但是我們無法進(jìn)行斷點調(diào)試。

通過上面這種Target方式創(chuàng)建靜態(tài)庫方式我們是可以直接斷點調(diào)試的,我們就可以一邊寫靜態(tài)庫一邊進(jìn)行調(diào)試,調(diào)試好后編譯生成Framwork再拖入項目,發(fā)現(xiàn)靜態(tài)庫有問題,我們就可以回項目進(jìn)行調(diào)試。

比如我們添加的target命名為 ZZAdSDKLib,那么項目里有自動生成如下

image.png

接著我們需要ZZAdSDKLib進(jìn)行如下設(shè)置

  • 設(shè)置Build Active Architecture Only 為No
  • 修改Mach-O Type 為 Static Library

因為Framwork可能是靜態(tài)庫也可能是動態(tài)庫,所以需要制定下ZZAdSDKLib的Mach-O Type 為 Static Library

image.png

接著就可以到ZZAdSDKLib目錄下編寫我們靜態(tài)庫的功能代碼了

image.png
  • 比如ZZSplashAd是我們自己的開屏廣告邏輯處理類
  • ZZSplashGDTAd是騰訊的開屏廣告處理類
  • ZZSplashToutiaoAd是頭條的開屏廣告處理類

接著我們需要把騰訊提供的.a靜態(tài)庫等文件和頭條的WMAdSDK.framwrok導(dǎo)入進(jìn)來

image.png

注意:

在添加三方Framwork庫時不要勾選Add to targets選擇
添加三方.a庫時需要勾選


image.png
image.png

說明:

這里ZZSplashToutiaoAd要依賴頭條提供的WMAdSDK.framwrok 靜態(tài)庫
這里有一點需要說明,第三方的framwrok靜太庫是不能直接打包到我們自己的靜態(tài)庫的,我們自己的靜態(tài)庫依賴頭條的framwrok靜太庫,所有制作好自己靜態(tài)庫后需要把自己的靜態(tài)庫和頭條的靜態(tài)庫都提供給使用者。

測試說明:

個人測試過,即使把三方framwrok靜態(tài)庫成功打包到我們自己的framwrok靜態(tài)庫,在外界使用我們自己的靜態(tài)庫時也是會報錯的

個人測試,直接把三方的.a靜態(tài)庫打包到我們自己的framwrok靜態(tài)庫,在外界是可以正常使用的 (網(wǎng)上也有說.a不能直接打包到framwrok靜態(tài)庫里,但是我測試是可以的,而且在外界用的也是正常的)

接著進(jìn)行編譯發(fā)現(xiàn),說頭條的文件找不到,但是我明明已經(jīng)導(dǎo)入了WMAdSDK.framwrok了

image.png

如果允許發(fā)現(xiàn)報類似這種Category方法找不到問題

-[UIButton setHitTestEdgeInsets:]: unrecognized selector sent to instance 

需要到Other Linder Flags 添加 -ObjC

image.png

因為三方的靜態(tài)庫依賴了一些系統(tǒng)的庫,所以接著需要到ZZAdSDK項目下添加相應(yīng)的系統(tǒng)庫

image.png

再次運(yùn)行發(fā)現(xiàn)報錯,因為雖然在創(chuàng)建的靜態(tài)庫里添加了WMAdSDK.framwrok,但是在上面ZZAdSDK項目里用的時候我們自己的靜態(tài)庫依賴WMAdSDK.framwrok,所以在上面也需要導(dǎo)入WMAdSDK.framwrok


image.png

在ZZAdSDK項目導(dǎo)入WMAdSDK.framwrok靜態(tài)庫


image.png
image.png

接著編譯成功,然后到Build Phases-》Header 下把需要暴露的.h 添加到Public下

image.png

再次編譯運(yùn)行成功


image.png

至此,我們就可以一邊開發(fā)靜態(tài)庫一邊調(diào)試了,調(diào)試好之后切換ZZAdSDKLib的Scheme為Release模式

image.png

然后編譯生成Framwork,到Products目錄下找到對應(yīng)的Framwork

image.png

腳本合成Framework

此時我們打包出的Framework是真機(jī)的Framework或者是模擬器的Framework
我們可以手動通過命令合成同時支持真機(jī)和模擬器的庫,也可以通過腳本自動合成

命令:
lipo -create 模擬器庫.framework的路徑 真機(jī)庫路徑 -output 合成庫的名字

腳本自動合成

image.png
image.png

粘貼腳本到Run Script


image.png

然后編譯就自動生成合并的framework庫了

以下是腳本文件

PROJECT_NAME='ZZAdSDKLib'
#要build的target名
TARGET_NAME=${PROJECT_NAME}
if [[ $1 ]]
then
TARGET_NAME=$1
fi
UNIVERSAL_OUTPUT_FOLDER="${SRCROOT}/${PROJECT_NAME}_Products/"

#創(chuàng)建輸出目錄,并刪除之前的framework文件
mkdir -p "${UNIVERSAL_OUTPUT_FOLDER}"
rm -rf "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework"

#分別編譯模擬器和真機(jī)的Framework
xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

#拷貝framework到univer目錄
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${TARGET_NAME}.framework" "${UNIVERSAL_OUTPUT_FOLDER}"

#合并framework,輸出最終的framework到build目錄
lipo -create -output "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${TARGET_NAME}.framework/${TARGET_NAME}"

#刪除編譯之后生成的無關(guān)的配置文件
dir_path="${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/"
for file in ls $dir_path
do
if [[ ${file} =~ ".xcconfig" ]]
then
rm -f "${dir_path}/${file}"
fi
done

#判斷build文件夾是否存在,存在則刪除
if [ -d "${SRCROOT}/build" ]
then
rm -rf "${SRCROOT}/build"
fi

rm -rf "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator" "${BUILD_DIR}/${CONFIGURATION}-iphoneos"

#打開合并后的文件夾
open "${UNIVERSAL_OUTPUT_FOLDER}"


關(guān)于如果處理靜態(tài)庫里的圖片等資源,需要創(chuàng)建資源Bundle,關(guān)于如何創(chuàng)建Bundle很簡單,參考下這個文章

接著把我們制作好的靜態(tài)庫、和依賴的三方靜態(tài)庫、創(chuàng)建好的資源Bundle拖入我們要用的項目里即可

image.png

因為.a已經(jīng)打包進(jìn)我們的靜態(tài)庫這里只拖入了三方的Framwork庫,當(dāng)然如果有依賴系統(tǒng)的庫還需要添加好系統(tǒng)的庫

最后說一下加入我們自己的廣告類ZZSplashAd里面 依賴第三方框架YYModel

#import "ZZSplashAd.h"
#import <NSObject+YYModel.h>

@interface ZZSplashAd()
@end

@implementation ZZSplashAd

- (void)loadAdAndShowInWindow:(id)window withBottomView:(id)bottomView {
      
}
@end

但是我有不想把“YYModel”打包進(jìn)我的靜態(tài)庫,此時就需要通過Cocoapods進(jìn)行管理,我們在podfile文件里添加target然后pod install

image.png

然后編譯ZZAdSDKLib靜態(tài)庫-》Show in finder -》找到生成的libYYModel.a 或者YYModel.framework

image.png

說明:我在測試過程中 有時候生成的是libYYModel.a 有時候是 YYModel.framework,還沒分析出原因,如果有知道的小伙伴歡迎交流。

最后,把我們自己打包的ZZAdSDKLib.framework、第三方依賴的framework、資源bundle、第三方庫的framework提供給使用者就可以了

image.png

說明:

比如我們把制作好的靜態(tài)庫拖到了A項目中使用,此時在A項目中必須要pod YYModel, 如果靜態(tài)庫里依賴和AFN,那么也必須pod AFN

-ObjC / -all_load / -force_load

我們在集成一些第三方靜態(tài)庫時,在集成文檔里有時會看到-ObjC ,-all_load,_force_load 這幾個東東,這幾個設(shè)置是什么意義那??

-ObjC

我們知道在Objective-C中方法調(diào)用都是在運(yùn)行期確定的,所以O(shè)bjective-C沒有針對每個方法定義鏈接符號,它只每個類創(chuàng)建鏈接符號。因此當(dāng)在一個靜態(tài)庫中使用類別來擴(kuò)展已有類的時候,鏈接器不知道如何把類原有的方法和類別中的方法整合起來,就會導(dǎo)致你調(diào)用類別中的方法時出現(xiàn)selector not recognized的錯誤。

設(shè)置ObjC標(biāo)志后,鏈接器會把一個類相關(guān)的所有目標(biāo)文件都加載進(jìn)來,這樣就解決了這個問題。由于這樣做會使可執(zhí)行文件體積變大,所以需要需要自己手動設(shè)置一下。

關(guān)于-ObjC蘋果的官方解釋是這樣的

-all_load

在64位ios應(yīng)用環(huán)境下,在靜態(tài)庫中只有category而沒有對應(yīng)的class定義時-ObjC標(biāo)志會失效(這是鏈接器的一個bug)
這時可以使用-all_load強(qiáng)制加載所有目標(biāo)文件 或者使用-force_load指定加載某一個包
千萬不要隨便使用這個參數(shù)!假如你使用了不止一個靜態(tài)庫文件,然后又使用了這個參數(shù),那么你很有可能會遇到ld: duplicate symbol錯誤,因為不同的庫文件里面可能會有相同的目標(biāo)文件
所以建議在遇到-ObjC失效的情況下使用-force_load參數(shù)

-force_load

-force_load所做的事情跟-all_load其實是一樣的
但是-force_load需要指定要進(jìn)行全部加載的庫文件的路徑
這樣的話,你就只是完全加載了一個庫文件,不影響其余庫文件的按需加載


參考文檔



xcode基礎(chǔ)配置詳解之Architecture(一)
-ObjC ,-all_load,_force_load的區(qū)別

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容