【iOS開發(fā)筆記】靜態(tài)庫制作及組件二進(jìn)制與源碼的切換方案

前言

移動(dòng)端項(xiàng)目復(fù)雜到一定程度都會(huì)走上組件化的道路,組件一多就會(huì)出現(xiàn)聯(lián)編緩慢的問題。對(duì)于Objectiv-C語言的項(xiàng)目,想要加速編譯打包的速度,就需要將大量依賴的組件在打包的時(shí)候使用靜態(tài)庫依賴,以加快編譯鏈接速度。

iOS項(xiàng)目進(jìn)行組件化,一般會(huì)使用cocoapods包管理工具,二進(jìn)制庫在iOS項(xiàng)目中,指的是靜態(tài)庫與動(dòng)態(tài)庫,當(dāng)組件提供靜態(tài)庫或動(dòng)態(tài)庫的時(shí)候,可以加速項(xiàng)目編譯與構(gòu)建,因?yàn)殪o態(tài)庫與動(dòng)態(tài)庫本身就是已經(jīng)編譯好的庫文件,從而能達(dá)到加速的目的。

同時(shí)在開發(fā)時(shí),經(jīng)常會(huì)需要多人協(xié)作開發(fā),對(duì)于組件和一些不常改動(dòng)的文件,使用靜態(tài)庫可以有效避免因?yàn)檎`改而產(chǎn)生bug的問題。

一、總體目標(biāo)及方案選擇

1 目標(biāo)

通過對(duì)本文接下來的內(nèi)容的學(xué)習(xí),我們希望最終的目標(biāo)主要有:

1. 將組件制作為靜態(tài)庫。

2. 通過cocoapods進(jìn)行管理,實(shí)現(xiàn)源碼和靜態(tài)庫的切換。


2 相關(guān)方案的對(duì)比和選擇

2.1 靜態(tài)庫類型選擇

靜態(tài)庫.framework.a的區(qū)別:

.a:只把代碼文件打包編譯成二進(jìn)制。

.framework:把代碼文件及其他資源,如圖片、音頻等文件,一起打包成二進(jìn)制。

選用何種二進(jìn)制類型,可以根據(jù)實(shí)際的項(xiàng)目情況進(jìn)行打包,在本文中,我們針對(duì)制作.framework進(jìn)行介紹。


2.2 打包方式選擇

靜態(tài)庫打包方式:

1.通過Xcode的打包方式,編譯打包。

2.使用Aggregate打包。

3.使用腳本直接打包。

4.使用第三方打包工具打包,如cocoapods-packager

考慮到簡單和易用,本文選用的是第二種Aggregate方式打包。


2.3 切換方案選擇

源碼和靜態(tài)庫切換方案:

1.podspec中使用if-else條件去區(qū)分源碼和靜態(tài)庫,但是在進(jìn)行切換時(shí),每次都需要pod cache clean,切換麻煩,也比較耗時(shí)。

2.使用Carthagecocoapods結(jié)合的方式,由cocoapods管理源碼,Carthage管理靜態(tài)庫,學(xué)習(xí)成本較高。

3.使用subspec實(shí)現(xiàn)源碼和靜態(tài)庫的切換,subspec主要是在cocoapods中給私有庫或第三方做目錄分層使用,在podfile中寫入制定subspec,可以只導(dǎo)入指定目錄下的文件。

綜合考慮下,本文選用第三種subspec的方式來實(shí)現(xiàn)切換,將源碼和靜態(tài)庫一起做成私有庫,分別放在兩個(gè)subspec下。


二、制作靜態(tài)庫

1 創(chuàng)建framework

1.1 將要制作為靜態(tài)庫的組件克隆到本地

本文內(nèi)容是在已有私有庫的基礎(chǔ)上,針對(duì)私有庫中的組件來制作靜態(tài)庫,所以下面將直接從制作靜態(tài)庫開始介紹。

在項(xiàng)目的根目錄新建一個(gè)project或者在項(xiàng)目中新建一個(gè)target。

1.1 新建project

新建project-1.png
新建project-2

如果希望制作.a文件,則選擇Static Library


1.2 新建target

新建target.png

新建target和project選擇其中一種方式即可。


2 設(shè)置framework

2.1 設(shè)置組件源碼

將組件的代碼文件引用到framework的target。

引用文件-1
引用文件-2

注意,這里不需要copy文件過去

2.2 其他設(shè)置

2.2.1 設(shè)置Build Settings

Architectures - 設(shè)置支持架構(gòu),這里armv7、arm64(64位ARM處理器)是真機(jī)架構(gòu),i386(32位模擬器)、x86_64(64位模擬器)是模擬器架構(gòu)。

設(shè)置build settings-支持架構(gòu)

iOS Deployment Target - 設(shè)置可支持的最低版本。

設(shè)置build settings-支持版本

Dead Code Stripping - 是否從framework中刪除未使用的代碼
Link With Standard Libraries - 是否鏈接蘋果標(biāo)準(zhǔn)庫
Mach-O Type - 這里的類型我們要選擇Static Library(靜態(tài))
Other Linker Flags(本文未使用) - 鏈接參數(shù),如果使用了category,最好加上Objc、all_load
Other C Flags(本文未使用) - 額外的C語言鏈接參數(shù),如果需要支持bitcode,需要加上-fembed -bitcode

設(shè)置build settings-Linking

Build Active Architecture Only - 是否只為當(dāng)前架構(gòu)編譯,NO則編譯所有架構(gòu)

設(shè)置build settings-編譯設(shè)置

2.2.2 設(shè)置Build Phases

將要暴露的頭文件移到public中

設(shè)置build phases

3 設(shè)置Aggregate

3.1 新建Aggregate

新建Aggregate

3.2 設(shè)置打包腳本

設(shè)置Aggregate打包腳本

3.3 編寫腳本內(nèi)容

將以下內(nèi)容復(fù)制進(jìn)Run Script中,修改第三行中的TARGET_NAME改為自己創(chuàng)建的framework名字,這段腳本會(huì)自動(dòng)合并真機(jī)和模擬器的二進(jìn)制文件。

#!/bin/sh
#要build的target名
TARGET_NAME='TenUIKitFramework'
#${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}-iphonesimulator/${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}"

4 打包

以上內(nèi)容設(shè)置完之后,就可以打包了,選擇Aggregate進(jìn)行編譯,編譯完成后會(huì)自動(dòng)打開編譯好的framework存儲(chǔ)的文件夾。

打包結(jié)果

5 靜態(tài)庫中使用cocoapods管理依賴的第三方

在之前建立的project中使用cocoapods導(dǎo)入第三方(pod init、修改Podfile、pod install),然后在要使用的地方導(dǎo)入頭文件,在framework中編譯一下,成功后則可以繼續(xù)第4步的打包流程。

三、使用cocoapods管理

1 移動(dòng)靜態(tài)庫到合適的位置

首先我們將剛才獲得的靜態(tài)庫文件夾移動(dòng)到根目錄,或者也可以不移動(dòng),我是為了等會(huì)設(shè)置路徑方便。

2 設(shè)置靜態(tài)庫與源碼的切換

下面我們通過編寫podspec文件來進(jìn)行設(shè)置,其中source代表源碼,framework代表靜態(tài)庫,pod導(dǎo)入時(shí)用于進(jìn)行區(qū)分,默認(rèn)使用framework。

Pod::Spec.new do |s|
  s.name             = 'TenUIKit'
  s.version          = '0.1.1'
  s.summary          = 'A short description of TenUIKit.'
 
  s.description      = <<-DESC
TODO: Add long description of the pod here.
                       DESC
 
  s.homepage         = '你的項(xiàng)目地址'
  # s.screenshots     = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
  s.license          = { :type => 'MIT', :file => 'LICENSE' }
  s.author           = { 'Ten' => '賬號(hào)' }
  s.source           = { :git => 'git地址', :tag => s.version.to_s }
  # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
 
  s.ios.deployment_target = '8.0'
 
  s.default_subspec = 'framework'
 
  s.subspec 'source' do |ss|
    ss.source_files = 'TenUIKit/Classes/**/*'
  end
 
  s.subspec 'framework' do |ss|
    ss.ios.vendored_framework = 'TenUIKit_Products/*.framework'
  end
end

3 收尾

這之后就可以push代碼到你的私有庫,在項(xiàng)目中使用時(shí),podfile中按如下寫法

#使用默認(rèn)framework
pod 'TenUIKit'
#使用源碼
pod 'TenUIKit', :subspec => ['source']

四、資源文件bundle的制作

framework只能包含頭文件和代碼,而不能包含圖片和storyboard等資源文件,這是就需要?jiǎng)?chuàng)建bundle來保存這些資源,以在其他工程中使用。


創(chuàng)建bundle文件有兩種方式,通過xcode創(chuàng)建或者自己手動(dòng)創(chuàng)建,下面我們將分別針對(duì)這兩種方式來進(jìn)行介紹。

1 通過xcode方式創(chuàng)建

1.1 創(chuàng)建target

首先在上文的工程中再添加一個(gè)target,選擇macOS中的bundle

新建bundle的target

1.2 設(shè)置Build Settings

Base SDK - 默認(rèn)是macOS用的,這里修改為iOS

設(shè)置bundle_build setting-SDK

Skip Install - 資源包是否需要安裝,這里我們選擇No不安裝

Installation Directory - 安裝路徑,不需要安裝所以這我們刪除

設(shè)置bundle_build setting-Deployment

1.3 設(shè)置Build Phases

將資源文件放進(jìn)bundle中

設(shè)置bundle_build phases

1.4 使用bundle

將bundle文件添加到framework所在的工程后,在framework中的Build Phases中添加bundle

在framework中添加bundle

1.5 項(xiàng)目中使用

使用時(shí)framework找到自己的bundle可以參考如下代碼:

[NSBundle bundleWithPath:[[NSBundle bundleForClass:self.class] pathForResource:@"TenPremissionsBundle" ofType:@"bundle" inDirectory:@"TenPremissionsFramework.framework"]];

2 自己手動(dòng)創(chuàng)建

這個(gè)方法也很簡單,只需要?jiǎng)?chuàng)建一個(gè)新文件夾,重命名為"xxx.bundle",即可創(chuàng)建出一個(gè)bundle文件,右鍵顯示包內(nèi)容,即可向里面添加資源文件,使用的步驟與使用xcode創(chuàng)建出的bundle文件的使用方法一樣。

手動(dòng)創(chuàng)建bundle
手動(dòng)創(chuàng)建bundle-顯示包內(nèi)容

3 framework與bundle的組合使用

3.1 項(xiàng)目中直接使用

上面講述的使用方式中,我們將framework和bundle打包在一起,這樣的好處主要是bundle隨時(shí)和framework綁定,避免了遺漏的情況。但相應(yīng)的也因?yàn)樾枰獙ramework拷貝到APP包中,增大了空間占用,完整的framework包也都暴露在APP包中

所以更建議使用frameworkbundle分離的方式,bundle文件不加入到framework工程下,framework中的Copy Bundle Resources下也不要添加bundle,分別將frameworkbundle導(dǎo)入APP中,這樣APP中的Copy Bundle Resources只需要添加bundle文件,而不需要添加framework


最后記得將在framework中查找bundle的代碼改成如下示例:

[NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"TenPermissionsBundle" ofType:@"bundle"]];

3.2 使用cocoapods管理

整體思路與上面相同,只需要將bundle文件與framework分開路徑存儲(chǔ),一起push到組件庫中。

framework與bundle示例
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評(píng)論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,559評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,581評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,922評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,096評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,639評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,374評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,591評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,789評(píng)論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評(píng)論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,322評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,554評(píng)論 2 379