iOS 二進制第三方庫思路之Carthage編譯靜態庫

											用Carthage編譯你喜歡的靜態庫

在這裏插入圖片描述

前言

Carthage的基本用法已經在上一篇文章詳細介紹了,本文主要針對一個問題的解決方案,那就是如何用Carthage編譯第三方庫爲靜態庫。和pod package一樣,封裝了xcodebuild進行編譯,省去了繁瑣的參數設置,提供更便捷方法使用,Carthage作爲後起之秀,維護力度和pod package相比,不是一個量級的,端午花了一天學習了下官方的文檔,可以確定Carthage將會是第三方庫打包成動態庫或者靜態庫的首選。由於項目都還是Objective-C爲主,因此我們不會全方位接入,而是使用它的打包功能,得力於他的松耦合,無侵入,可以配合Cocoapods結合使用,賊好用。

問題

Carthage默認只能編譯成動態庫。

解決方案

  • Carthage 0.29.0以下

staticFramework腳本起了一個臨時文件xcconfig,寫入如下信息進行靜態庫的編譯參數設置

LD = the/path/to/ld.py
EDBUG_INFOMATION_FORMAT = dwarf

該方法是老版本製作靜態庫的方法, 但是在Xcode 9.4之後,原生已經支持了Swift源碼的靜態庫,我們只需要修改對應的mach-o type即可。

  • Carthage 0.30.0之後
    當前版本以及到最新的版本,都已經支持了OCSwift的靜態庫,即使是已經支持了,但是如果你自己做一個庫,要支持Carthage,默認的mach-o類型還是要是動態庫類型,那麼和之前的第三方庫的製作和動態庫基本沒有任何區別。
  1. 庫工程的project要支持shared schemes,Product > Schemes > Manage Schemes > Shared
  2. 使用的時候和動態庫一樣都需要手動拖入Build phase中的 Linked Binary,咱們等下用local pod 來做,都不需要手動拖

那麼步驟如下:

  • 當我們check out對應的庫源碼後,找到對應project下面的target,Build SettingsLinking sectionMach-O Type改爲Static Library
  • 執行carthage build --no-use-binaries --platfor ios,就可以在./Carthage/Build/$(PLATFORM_NAME)/Static下面生成對應的靜態庫
  • 默認的動態庫我們需要添加腳本carthage copy-frameworks,咱們現在用的Static Framework不需要了,直接拖入即可

Why Static Frameworks

說了那麼多,那麼爲什麼我們這裏要選用第三方庫的靜態庫作爲目標文件呢?
首先如果你注入太多動態庫,App的啓動時間就會變慢,也就是main函數之前動態鏈接的過程變久了。因此我們這裏用Carthage來把動態庫編譯成靜態庫的方式解決這個問題。如果全部是靜態庫,那麼都會被鏈接到App包裏面,這樣看起來包會增大,還有一個方案就是,把多個靜態庫通過一個動態庫的工程包裹,包成一個動態庫進行鏈接,這樣靜態庫不會被打入包裏面,而且也不會有太多的啓動耗時。這裏需要注意的是,也不是和無腦合併使用動態庫,Apple給我們建議是最多6個動態庫是最合適的傳送門

關於靜態庫合併成動態庫需要注意以下:
直接起一個動態庫的project,然後把我們需要組合的靜態庫都拖入Link Binary with Libraries中,爲了保證所有的靜態庫都會被合併,需要在Build SettingsOTHER_LDFLAGS中加入-all-load參數,強制鏈接所有靜態庫的全部內容,而不是有時候只鏈接合併其中一部分而已。

腳本自動編譯靜態庫

根據上面的介紹,我們需要手動修改CarthageCheckouts中目標Target下對應的Mach-O Type類型爲Static Library,這看上去沒有問題,但是如果庫更新後,我們的Checkouts源碼又會被重置,這顯然很不優雅,如果哪天忘記修改了,又變成動態庫了,因此就有了如下腳本。

先介紹下腳本核心參數:

  • Mach-O Type
    Build Setting中的MACH_O_TYPE決定了目標文件被編譯成哪種。
  1. executable 可執行文件,也就是我們平時的App架構
  2. dynamic library 動態庫
  3. static framework 靜態庫 咱們現在要的就是這個 ✅ staticlib
  4. bundle 資源文件
  5. relocatable object file 這種據我瞭解是 多個動態庫或者靜態庫的合併
  • Debug infomation format
    要保證xcodebuild不會生成對應的dYSM調試文件,否則的話會編譯失敗。因爲靜態庫本身已經包含了debug symbols當靜態庫被集成到executable的app架構中時,App最終打包生成的可執行文件,也會包含dSYM文件,該文件包含了本身App的符號以及靜態庫的符號,所以我們需要把DEBUG_INFORMATION_FORMAT僅僅設置dwarf即可

  • Framework search paths
    Carthage如果編譯的是靜態庫,會額外生成一個/Static的文件夾存放對應的靜態庫,比如/Carthage/Build/iOS/Static
    其實正常情況下都不需要設置,所以可以認爲該選項是可選的,主要是因爲庫之前有依賴關係,比如A依賴B這種,可能會發生編譯失敗的情況,因此主動設置下對應的path,也是問題不大的。我們應該不會遇到這個問題,等下我是直接用Cocoapods來管理這些編譯好的庫文件,我們還是和之前一樣,熟練的編寫podspec文件依賴即可,簡直不好太輕鬆。

Demo演示

  • 1.創建工程
pod lib create CarthageProj
  • 2.創建Cartfile
vim Cartfile
# 寫入如下兩個庫
# github "AFNetworking/AFNetworking" == 3.1.0
# github "Masonry/Masonry" == 0.6.3
  • 3.Check out
carthage update --no-use-binaries --platform iOS --no-build
*** Fetching Masonry
*** Checking out AFNetworking at "3.1.0"
*** Checking out Masonry at "v0.6.3"

該腳本和之前的不同,跟了三個參數,分別代表不用遠程編譯的預編譯二進制,只用本地的源碼編譯,第二個參數代表架構,第三個代表不進行編譯,只要把源碼check out即可,編譯我們等下用自己的腳本進行編譯,別忘了我們上邊是需要用到臨時的xcconfig文件編譯靜態庫的,這裏如果直接編譯,那就是默認的動態庫

  • 4.封裝編譯腳本
./build-static-carthage.sh -p ios -d AFNetworking Masonry

-p | --platform
必須指定ios,macos,watchos,tvos其中之一,而且只能指定單個

-d | --dependencies
需要編譯的Framework數組,從Cartfile文件中拿就行了

腳本輸出日誌如下:
Building static frameworks with Xcode temporary xconfig file:
/tmp/static.xcconfig.4GLgSU

With contents:
MACH_O_TYPE = staticlib
DEBUG_INFORMATION_FORMAT = dwarf
FRAMEWORK_SEARCH_PATHS = $(inherited) ./Carthage/Build/iOS/**

Building with command:
carthage build --no-use-binaries --platform ios AFNetworking Masonry 

*** xcodebuild output can be found in /var/folders/wh/hhkfnl5d3_j9lsvyjrdcfnv80000gn/T/carthage-xcodebuild.Cpoz1c.log
*** Building scheme "AFNetworking iOS" in AFNetworking.xcworkspace
*** Building scheme "Masonry iOS" in Masonry.xcworkspace

當腳本編譯完後,我們需要的靜態庫就出現在了/Cartahge/Build/iOS/Static

  • 5.引入工程

A、不用cocoapods
我們需要手動引入,拖入項目中或者Link Binary那裏,然後最重要的是你要手動生成一個xxx.xcconfig的文件,然後在project中的Configurations中比如DebugRelease中指定對應的xcconfig文件名稱,其內容如下

FRAMEWORK_SEARCH_PATH = $(PROJECT_DIR)/ ../Carthage/Build/iOS/Static
OTHER_LINKER_FLAGS = -all_load -ObjC

FRAMEWORK_SEARCH_PATH 指定了庫文件的搜索路徑
OTHER_LINKER_FLAGS 告訴編譯器要把靜態庫完完全全的鏈接到可執行文件中來。-ObjC代表庫文件中包含了Objective-C extensions

B、使用cocoapods
這裏爲了Demo簡單,我們用local pod進行演示,效果和遠程pod是一樣的,只是遠程需要lint以下。
新建一個文件夾Library,把剛纔生成的庫都給導入到這裏,然後對應的庫下面寫一個podspec文件,該文件從哪來呢?正好,直接從Carthage/Checkouts中的源碼中有,只是他作用的依賴是源碼,我們把它複製過去,修改成依賴vendored_frameworks的方式,所有的靜態庫都這麼操作
在這裏插入圖片描述
然後編寫Podfile文件

# Uncomment the next line to define a global platform for your project
platform :ios, '10.0'

target 'CarthageProj' do
  
  pod 'AFNetworking', :path => 'Library/AFNetworking'
  pod 'Masonry', :path => 'Library/Masonry'

  target 'CarthageProjTests' do
    inherit! :search_paths
    # Pods for testing
  end

end

執行 pod install,打開CarthageProj.xcworkspace
在這裏插入圖片描述
可以看到,這幾個xcconfig文件是cocoapods幫我們生成的,也就是和我們自己手動添加一樣道理,只是這裏的參數更多更全,原理是一樣的,所以大家熟悉cocoapods的話,組合起來用,是不是非常爽。

  • 最終目的
    在這裏插入圖片描述
    通過靜態庫的依賴,撇去了源碼的依賴,可以看到我們對AFNetworkingMasonry已經沒有任何文件的編譯信息了,這就是二進制化的好處,也是我們後續要做的,優化項目編譯打包時間。

結論

通過這個端午的研究,發現通過Carthage打包靜態庫是個可行的方案,至少打包第三方庫是可行的。二進制方案的一個環節算是又有了一個新的思路。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章