近來公司的公共庫里有點小問題,但是公共庫打成了framework,即使手上有源碼也很難調試。網上百度了很多方法,有臨時方法,也有比較好的方案,寫一篇博客記錄下來,送給正在調試framework的你,哈哈哈。
所以呢,這篇文章中你會看到:
- 調試framework的臨時方案
- framework的制作方式
- 推送庫到cocoapods
- Pods庫的二進制切換
調試framework的臨時方案
這個方案呢,只能作為臨時方案,因為這個方案還是有一定局限性的。先說實現方式吧。
首先呢,framework的庫調試的痛苦在于第一你打不了斷點,第二你也看不到堆棧信息。
所以從兩方面入手逐個擊破就好。
首先如果你有源碼的話,只要打開你源碼的工程build一下,你就會得到一個framework文件。
-
看不到堆棧信息
這是因為framework打包的時候不在你的電腦上,所以你的電腦并沒有framework對應的符號表,你就看不到信息了,所以很簡單的方式就是右鍵show in finder拿到framework然后替換你主工程中響應的framework就好了,這時候你已經可以看到堆棧信息了。
- 無法打斷點
至于打斷點這個就比較玄幻了。首先打開你的主工程,然后從你framework的源碼中把你要打斷點的.m文件拖到主工程里下圖的位置。為打開文件但不會引入文件,這個時候你打下斷點試試你就會發現神奇的居然進入了斷點。
但是這種方式的缺點是,xCode關了你就還需要重新弄一遍,而且提交代碼前要記得把framework替換回去。所以再請教了一個大神之后,他告訴我一個二進制切換方案,自己試了下很好用。由于這種實驗我不可能那公司的公共庫做實驗,所以就完全自己從framework制作開始走了一遍流程。這里都記錄一下。
framework的制作方式
1.framework制作在新版本的xcode上已經十分簡單了。首先創建工程的時候選擇Cocoa Touch Framework。
然后他會自動生成一個頭文件的.h,我這里不想重新走一遍流程就偷懶用后面的圖了。
2.然后你就開始創建你要打framework的文件就好,在別的地方寫好了拖進來也好。然后在自動生成的頭文件中引入你想對外暴露的.h文件就好。至此代碼層級的事情就完成了。
3.接下來你需要再做3個buildsetting的設置。
4.然后你要設置你對外暴露的文件
5.接下來,如果你的庫只想支持模擬器就選擇模擬器build一下,想支持真機就Generic iOS Device build一下。
6.build成功之后再Products文件夾下則會生成一個framework文件。如果你是只支持某一種平臺的話到這里已經結束了,但是如果你要支持真機和模擬器的話,我們還要將framework合并。這里講一下合并的方法。就是終端輸入一段命令:
終端指令:lipo -create +上面兩個文件的路徑 +-output+ 合成后文件的輸出路徑
例如我的是這個樣子的:
這是將剛剛生成的文件替換到兩個framework中的任意一個,然后被替換的framework就是支持兩種平臺的了。
更為詳細的教程你可以看這里,《Xcode9.0 制作.framework》。
推送庫到cocoapods
我說一個我做庫的目錄結構吧。
一般情況下我會這樣,建一個根目錄A,然后根目錄下存放兩個文件夾,一個叫Demo,一個叫你的庫的名字,如DWFlashFlow。然后Demo里面放一個你庫的使用Demo就好,以庫命名的文件夾里面存放庫文件。根目錄下存放spec,最后連同根目錄一起傳到一個遠程倉庫中。
文件編輯好了我們來編輯一下PodSpec。
Pods為我們提供了很多可選項,讓你有豐富的定制可能,這里我說一下我常用的及必須的幾個選項。我發一下我的config,你可以直接做模板改就好了
Pod::Spec.new do |s|
s.name = 'DWFlashFlow'
s.version = '1.0.1'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.summary = '網絡請求庫,核心基于AFN3.0,實現批量請求、鏈請求及依賴請求。Network request library, core based on AFN3.0, implements batch request, chain request and dependency request.'
s.homepage = 'https://github.com/CodeWicky/DWFlashFlow'
s.authors = { 'codeWicky' => 'codewicky@163.com' }
s.source = { :git => 'https://github.com/CodeWicky/DWFlashFlow.git', :tag => s.version.to_s }
s.requires_arc = true
s.ios.deployment_target = '7.0'
s.source_files = 'DWFlashFlow/**/{DWFlashFlow,DWFlashFlowManager,DWFlashFlowBaseLinker,DWFlashFlowAFNLinker,DWFlashFlowAbstractRequest,DWFlashFlowRequest,DWFlashFlowBatchRequest,DWFlashFlowChainRequest,DWFlashFlowCache}.{h,m}'
s.frameworks = 'UIKit'
s.dependency 'DWNetworkAFNManager', '~> 1.0.0'
end
對照著在Pod search時看到的字段有些屬性你自然就懂了。
這里著重說一下source_files這個字段的規則。
DWFlashFlow/**/{DWFlashFlow,DWFlashFlowManager,DWFlashFlowBaseLinker,DWFlashFlowAFNLinker,DWFlashFlowAbstractRequest,DWFlashFlowRequest,DWFlashFlowBatchRequest,DWFlashFlowChainRequest,DWFlashFlowCache}.{h,m}
首先路徑是相對于PodSpec所在的目錄的,因為我們將Spec放在了根目錄下,并且根目錄下的DWFlashFlow文件夾下方的是我們的庫文件,所以第一個目錄我寫了一個DWFlashFlow,第二個路徑是兩個星號,這代表搜索方位將是當前文件夾下的所有文件(包括子文件夾中的文件)。前兩個路徑就聲明了當前庫文件的搜索范圍就是根目錄下DWFlashFlow中的所有文件。接下來在大括號之間的內容就是我們庫文件的文件名在這些字符串間選擇,然后后面的大括號之間是庫文件的擴展名在這之間選擇,通過這個路徑,我們就確認了所有庫文件的文件名。
另一個字段是source:
s.source = { :git => 'https://github.com/CodeWicky/DWFlashFlow.git', :tag => s.version.to_s }
分成兩部分,前面一部分是告訴pods去這個地址拉取文件,當然就是填你遠端的倉庫地址啦,后面的tag就是告訴pods你要拉取的版本。這里你如果就像我這么寫的話,就是取s.version的值了。
Spec文件是pod識別庫的唯一文件,制作好了我們就要開始上傳了。
1.首先如果你要發布的版本是0.0.1版本的話就給當前庫打一個0.0.1的tag。然后推到遠端。
2.spec中version改為0.0.1。
2.5 這時如果你是第一次制作的話你還要注冊一下。
執行命令pod trunk register 郵箱地址 ‘用戶名’ –verbose
3.本地校驗一下庫的合法性
cd到庫的根目錄,然后終端執行pod spec lint XXX.podspec
這一步會報warning和error,根據信息去修改就好了,如果你想忽略警告的話,命令后面記得加 --allow-warnings(第一個是兩個短橫線,第二個是一個)
4.上傳庫
終端執行pod trunk push XXX.podspec
然后你就可以開始等待了,當出現這個頁面時就是上傳成功了。
我沒有圖,copy一個。
成功以后你就可以pod search 一下啦。如果你是第一次發布當前庫的話,你要執行清除索引命令,因為索引是在上一次沒有索引的情況下調用search生成的,里面不會有你的新庫的信息,所以要清除舊的索引。
rm ~/Library/Caches/CocoaPods/search_index.json
并且此時執行下pod repo update
來更新一下倉庫,再執行search就會搜索到你的庫了,如果沒搜到,那就是你弄錯了。
更詳細的公共庫制作方式你可以看這里,《將代碼提交到CocoaPods超詳細的操作步驟和圖解
》。
上面的步驟告訴了你如何上傳至cocoapods的公共庫,接下來我會再說一下上傳到你的私有庫的方法。
首先打開到.cocoapods/repos
目錄下。
正常的話如果你沒有私有庫的話,你應該只有一個master文件夾。
在你的遠端倉庫中創建一個私有倉庫,叫什么隨你便了,復制一下倉庫地址。
然后執行比如說你私有庫的名字是REPO_NAME,遠程倉庫地址是SOURCE_URL,就執行命令
pod repo add REPO_NAME SOURCE_URL
這時候repos目錄下應該會多一個你剛才創建倉庫名的文件夾。
這里要解釋一下,你剛剛創建的倉庫只是作為私有庫版本管理庫存在,他應該是一個空的倉庫,而不是你要做私有庫的那個地址,希望你清楚。
然后你有了私有倉庫,spec文件跟共有庫是一樣的,只是推得時候命令不一樣,這時候你要用的命令是
pod repo push REPO_NAME xxx.podspec
其他都一樣,這時候你已經可以通過pod search 搜索到你的私有庫了。
不過當你需要安裝庫的時候你的podfile還需要做一定的改動,就是要告訴pod你的倉庫的實際地址。
也就是說你的podfile大概是這個樣子的:
source 'https://github.com/CocoaPods/Specs.git'
source 'SOURCE_URL'
target 'TestWork' do
pod 'Test_Frm_Private', '~> 0.0.1'
end
至此你也可以安裝私有庫了。
如果你想看到更詳細的私有庫推送,你可以看這里,《如何創建私有 CocoaPods 倉庫》。
Pods庫的二進制切換
先說很重要的一個,二進制切換只支持私有庫,這是大前提,一會解釋原因。
終于走到最后一個流程了,其實podSpec是支持條件語句的,比如說這樣:
Pod::Spec.new do |s|
s.name = 'Test_Frm_Private'
s.version = '0.0.1'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.summary = 'A test for pods.'
s.homepage = 'https://coding.net/u/codeWicky/p/Test_Frm_Private'
s.authors = { 'codeWicky' => 'codewicky@163.com' }
s.source = { :git => 'https://git.coding.net/codeWicky/Test_Frm_Private.git', :tag => s.version.to_s }
s.requires_arc = true
s.ios.deployment_target = '7.0'
s.frameworks = 'UIKit'
$lib = ENV['use_lib']
if $lib
puts 'Install Test_Frm_Private via Framework'
s.ios.vendored_framework = "Test_Frm_Private/Framework/Test_Frm_Prvt.framework"
else
puts 'Install Test_Frm_Private via SourceCode'
s.source_files = 'Test_Frm_Private/SourceCode/**/{Test_Frm_Prvt,Test_Frm_Prvt_Handler}.{h,m}'
end
s.preserve_paths = "Test_Frm_Private/**/*"
end
如上,當Podspec是上面這個樣子的時候我們可以通過use_lib=1 pod install
讓他命中if的第一個分支。原理大概就是pods會把pod前的所有字段作為一個字典供podSpec使用。既然有了條件分支,我們的目標就是根據不同條件改變pods的不同資源了。吶Spec的代碼也比較清晰,沒什么多余說的,只是framework資源的引入跟.h.m的圖引入是有一點小小的區別的,就是對應的字段不一樣。注意一下就好了。
那為什么說只有私有庫能做這種切換呢?是因為只有podspec支持條件語句,但是當你傳到共有庫時podspec會被轉換成podspec.json。目的也很清楚,開發者既然已經封裝成framework了就一定不想讓你看到.h.m,所以這個接口就沒有給公共庫留出來,只有私有庫可以。
至此,你就可以通過use_lib=1 pod install
加載framework形式的庫,pod install
加載.h.m的庫了。
如果你想看更多關于二進制切換的內容,來這里吧《Pod二進制化
》。
好吧,為了追查framework中的一個問題,我饒了如此大一圈,你可能會說既然有緣嗎為什么不直接引入.h.m的形式,原因是事實上framework的編譯速度會高于.h.m形式的速度,并且一般較大的公司的庫都是很成熟、經歷了多個版本的修改的不希望你隨意去改變的庫,所以封裝成framework也很方便管理,所以才有了這方面的需求,那就是這樣。我也就當記錄一下全過程,免得自己忘了。吶,這次沒有廣告啦,拜拜。
參考資料: