前言:
本文是我在工作中開發 Framework 過程中的對踩過的坑走過的彎路的總結,此教程 Swift 和 OC 都適用,文章末尾附可能遇到的問題以及解決方案,希望給各位開發小夥伴一點幫助。
本文主要講了一下內容:
- 動態庫和靜態庫的區別
- 如何建立 swift 版的 framework
- 如何除錯 Framework,兩種方法
- 關於 Framework 的CPU架構 以及如何合成 framework 的架構讓其即支援真機又支援模擬器,兩種方法
- 在開發 framework 中可能遇到的坑以及解決方案
開始:
在我們開發中最離不開的就是 Framework 比如 UIKit.framework,所以對 framework 應該是比較熟悉的,那麼在開發中也經常把自己的所做模組的程式碼做成 framework,場景如下:
- 方便給別人使用我們自己的模組
- 提供給第三方使用,且又不願意別人看到自己的內部實現邏輯
- 模組化提高程式碼的複用性
動態庫和靜態庫的區別
靜態庫:
連結時完整地拷貝至可執行檔案中,被多次使用就有多份冗餘拷貝。
動態庫:
連結時不復制,程式執行時由系統動態載入到記憶體,供程式呼叫,系統只載入一次,多個程式共用,節省記憶體。
區別:
靜態庫和動態庫是相對編譯期和執行期的:靜態庫在程式編譯時會被連結到目的碼中,程式執行時將不再需要改靜態庫;而動態庫在程式編譯時並不會被連結到目的碼中,只是在程式執行時才被載入,因為在程式執行期間還需要動態庫的存在。
總結:
同一個靜態庫在不同程式中使用時,每一個程式中都得匯入一次,打包時也被打包進去,形成一個程式。而動態庫在不同程式中,打包時並沒有被打包進去,只在程式執行使用時,才連結載入(如系統的框架如UIKit、Foundation等),所以程式體積會小很多,但是蘋果不讓使用自己的動態庫,否則稽核就無法通過。
下面就一步一步的演示如何製作一個 framework, 以及製作自己的 framework 中可能會遇到的坑,以及如何解決。作為演示做個比較簡單的陣列亂序的 framework
新建一個 framework
- File -> new -> Progect-> 選中 iOS -> Cocoa Touch Framework
- 點選 Next -> Protect Name 為 ArrayDisorderSDK launguage 選為 Swift 點選 Next 新建完成
- 在 Framework 中新建檔案: 選 source 的時候 可以選擇Cocoa Touch Class 或者Swift File 等 都是可以的可以根據自己的需要選擇
類名就取為 ArrayDisorder,然後新建完成
- 在ArrayDisorder 類中寫下陣列亂序的程式碼
open class ArrayDisorder: NSObject {
open func disorder (orders:Array<Any>) -> Array<Any> {
var temp = orders
var count = Int(temp.count)
while count > 0 {
let index = Int(arc4random_uniform(UInt32(Int32(count))))
let last = Int(count-1)
temp.swapAt(index, last)
count -= 1
}
return temp
}
}
複製程式碼
注意:也許你會發現 在 class 前面或者在disorder方法前有 open 關鍵字,說到這裡先說下和這個地方和oc 製作 Framework 不同的地方
- oc 製作 framework 會生成一個和 framework 名字一樣的類 只有.h 檔案 內容如下
//! Project version number for ArrayDisorderSDK.
FOUNDATION_EXPORT double ArrayDisorderSDKVersionNumber;
//! Project version string for ArrayDisorderSDK.
FOUNDATION_EXPORT const unsigned char ArrayDisorderSDKVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <ArrayDisorderSDK/PublicHeader.h>
上面的這句註釋說的很清楚 如果你想讓別人外面找到你在 framework 中的類你必須像 <ArrayDisorderSDK/PublicHeader.h> 這種格式一樣把 你的類匯入這檔案中
複製程式碼
最後一句
// In this header, you should import all the public headers of your framework using statements like #import <ArrayDisorderSDK/PublicHeader.h>
複製程式碼
說的很清楚如果你想讓別人外面找到你在 framework 中的類你必須像 <ArrayDisorderSDK/PublicHeader.h> 這種格式一樣把 你的類匯入這檔案中,在編譯成 SDK 之後生成的標頭檔案中就可以看到你對外開放的類和方法
建立 Swift framework也會生成一個這樣的檔案但是 則不需要這樣匯入標頭檔案。只需要在要暴露給使用者的類名和方法名前面寫上 Open 或者 Public 當編譯成 Framework 之後會生成 "你的 framework 的名字-Swift.h"的檔案 在這個檔案中你可以看到類名前面 表有 Public 或者 Open 關鍵字的類.
什麼時候用 Open 什麼時候用 Public?
-
Open 在作用域外是可以被訪問,繼承 ,用 Open 關鍵字修飾的開放類成員在作用域之外是可訪問和可覆蓋的。
-
Pubic 在作用域外是可訪問的,但在作用域之外沒有子類。公共類成員是可訪問的,但在作用域之外是不可覆蓋的。
配置你的 Framework
- 配置動態或者靜態庫
點選 framework 的 target build settings - linking -> Mach-o Type -> Static Library 或者 Dynamic Library
- Development Target
在這個 Demo 中 設定為支援 iOS 8
- 還要配置 執行編譯的成的 Build Configuration 為 release 如果不是 release 則在 release 環境下執行會出錯
走到這一步,基本 Framework 已經初步完成, Command + B
注意:編譯的時候 選擇的 Device 如果是模擬器 則生成的是 x86架構,僅支援模擬器,在真機上這個編譯的 Framework 則不能用 ,如果 Device 不選或者選擇你連線在電腦上的真機,則編譯成的是arm64 可以在真機上跑但是不能用在模擬器,如果製作的 framework 需要在模擬器和真機上用,則需要把這兩個架構合成一塊,這個==待會下面詳細講.==
點選生成的 Framework 然後 Show In Finder
如圖中所示,生成了ArrayDisorderSDK.framework 那麼這個檔案就可以直接給別人使用了.
除錯 Framework
在我們寫程式碼時一般都習慣於邊寫邊除錯,那麼在製作 Framework 時有兩種方法除錯我們的 Framework
第一種(不推薦)
新建一個名字是 testArrayDisorderDemo的 Progect 如下
直接把編譯好的 Framework 拖入到 testArrayDisorderDemo中
然後 import ArrayDisorderSDK
在 ViewDidLoad 中寫入如下程式碼
let disOrder = ArrayDisorder() //Framework 中的封裝了亂序功能的類
//disOrder.disorder(orders: [1,2,3,4,5,6,7,8,9]) 呼叫ArrayDisorder 對外公佈的類
print(disOrder.disorder(orders: [1,2,3,4,5,6,7,8,9]))
// 輸出:[8, 6, 2, 5, 3, 4, 7, 9, 1]
複製程式碼
到此,我們簡單的 framework 就可以算完成了.
但是這中除錯framework 的方法效率比較低,我們每一次除錯都需要重新把原來的 framework 從testArrayDisorderDemo中刪除然後重新匯入.這樣比較麻煩.那麼下面有一種比較方便高效的方法。
第二種 建立一個依賴工程(推薦)
還是testArrayDisorderDemo 這個測試 Framework 的工程
如圖所示
然後把ArrayDisorderSDK 拖入到testArrayDisorderDemo 檔案中 如圖
然後開啟testArrayDisorderDemo
-- > General ->Embedded Binaries-> 點選加號-> add Other -> 選中ArrayDisorderSDK.xcodeproj
如下圖
)]
再選擇 Target 的地方你會發現 現在已經有連個 Taeget 一個是testArrayDisorderDemo 一個是ArrayDisorderSDK
如果選中 testArrayDisorderDemo就是執行 Demo 如果選中ArrayDisorderSDK 就是編譯 SDK
這時你會發現可以直接在testArrayDisorderDemo 的 ViewController 中直接 匯入ArrayDisorderSDK就可以了 不需要在刪除然後再拖入了. 最重要的是 ,當你選中的testArrayDisorderDemo Tagret 執行的時候 當程式走到 framework 中去的時候 你還可以打斷點等方式除錯. 效率會很高.
注意: 當你修改 framework 中的程式碼時要想測試下修改的效果,你必需先編譯下你的 framework 的 target 如本文中所示的就要先編譯下ArrayDisorderSDK的 target,不編譯的話就相當於你的 測試你 framework 的 Demo 用的還是原來的 framework 而不是修改後的
關於 Framework 的CPU架構
CPU 的架構在不同的機型上有不同的 主要有一下:
arm7: 在最老的支援 iOS7的裝置上使用
arm7s: 在 iPhone 5 和 iPhone 5c 上使用
arm64: 在 iPhone 5s 的64位 ARM 處理器上
i386: 在32位模擬器上使用
x86_64: 在64位模擬器上使用
當然一個 Framework 不需要全部支援,可以根據需要.
如上文所說, 編譯的時候 選擇的 Device 如果是模擬器 則生成的是 x86架構,僅支援模擬器,在真機上這個編譯的 Framework 則不能用 ,如果 Device 不選或者選擇你連線在電腦上的真機,則編譯成的是arm64 和 arm7 可以在真機上跑但是不能用在模擬器,如果製作的 framework 需要在模擬器和真機上用,則需要把這兩個架構合成一塊,那麼這就需要合成架構了.
首先,我們要了解,如何檢視一個 framework 的架構
用命令: lipo -info
注意: -info 中 - 和 info 沒有空格,info 後面有一個空格
如圖所示
把ArrayDisorderSDK 檔案拖入控制檯 然後回車 控制檯就可以輸出ArrayDisorderSDK 的 CPU 架構 如 armv7 arm64
這個是支援真機的,如果支援模擬器則不行的,需要重新編譯:
在上圖中也能看到 當選中ArrayDisorderSDK.framework show in Finder 時你會發現在上一層有四個資料夾(最多為四個如果你只在 relesae 下真機上編譯 就只有一個資料夾) ,主要分為 Debug 和 release 兩種環境下的 真機和模擬器 Debug-iphoneos/ Debug-iphonesimulator Debug 下的模擬器 Release-iphoneos/Release-iphonesimulator relesase 下的真機和模擬器
那麼我們需要做的就是把Release-iphoneos/Release-iphonesimulator 下的兩種架構合成一個
架構合併:
第一種方法(不推薦)
用命令: lipo -create aa bb -output cc 加入你framework 的名字是 ArrayDisorderSDK
aa: 表示Release-iphoneos 資料夾下的 ArrayDisorderSDK.framework/ArrayDisorderSDK 的路徑 bb : Release-iphonesimulator 資料夾下的 ArrayDisorderSDK.framework/ArrayDisorderSDK
cc: 生成的檔名 是和你的 framework 同名的,比如叫ArrayDisorderSDK
生成的output 出來的ArrayDisorderSDK是需要把Release-iphoneos 資料夾下的 ArrayDisorderSDK.framework 的中的ArrayDisorderSDK 替換掉的.
注意:-create 以及-output 在 create 和 output前面以- 是沒有空格,但是在create 和 output後面是有一個空格的
上面說的可能不太好懂,那麼下面演示一下,化繁為簡: 如圖
總之這種方法還是太麻煩的,容易出錯,下面介紹另外一種比較簡單的.
第二種:(推薦)
- 選中ArrayDisorderSDK.xcodeproj 新建一個名字為UniversalArrayDisorder 的 target
- 選中 UniversalArrayDisorder --> Build Phases --> 選中加號 --> 選中 New Run Script Phase
然後在 指令碼地址
下載一個名為universal-framework.sh 檔案 把檔案內容拷貝到如圖所示
注意:universal-framework.sh 中 ${PROJECT_NAME} 要把這個換成你自己的 framework 的名字
設定完成之後 ,編譯UniversalArrayDisorder target 編譯成功之後,會自動開啟一個生成 Framework 的資料夾,然後再檢視架構資訊你會發現,即支援真機也支援模擬器啦.
你可能會遇到的坑
1 找不到 framework
錯誤提示:
No such module xxx
複製程式碼
解決:
原因就是 Framework Search Path 中的路徑錯了 可以參考 參考地址 解決方案手動設定 這個路徑是你向引用你的 framework 的專案拖自定義的 framework 時自動生成的 所以簡單的解決方案就是 刪除 framework 重新拖入 注意:拖的時候一定要確保 framework 和目標專案在同一個資料夾下,這樣就不會出問題了
2 找不到類
錯誤提示:
'xxxx' is unavailable: cannot find Swift declaration for this class
xxxx 表示 framework 中的類名
複製程式碼
解決:
framework 的架構錯誤: 如果如你的 framework 需要在模擬器上跑 那麼你的 framework 必須包含x86 如果還需要在真機上跑 那麼必須包含arm64 架構 可以用命令檢查架構 lipo -info 參考
3 動態庫和靜態庫的問題
錯誤提示:
Reason: image not found
Message from debugger: Terminated due to signal 6
複製程式碼
解決:
動態庫 Embedded Binaries沒有 新增 your framework name.framework -> 新增 如果是靜態庫 則不需要新增 如圖
寫在最後:
參考文獻:
-
:https://medium.com/flawless-app-stories/getting-started-with-reusable-frameworks-for-ios-development-f00d74827d11
-
:https://medium.com/captain-ios-experts/develop-a-swift-framework-1c7fdda27bf1
-
:https://www.raywenderlich.com/65964/create-a-framework-for-ios
本文是在我開發中遇到的問題的一個總結,總體傾向與如何更方便高效的建立自己的 framework,對於 OC 和 Swift 建立 framework 的異同介紹較少,由於本文重點不在於此,有機會下次再總結. 由於工作繁忙,水平有限,難免有不全,或者說的不合適的地方,還請看到此文章的朋友不吝賜教.或者你對本文中有不理解的地方,都希望在評論區交流. 如果此文解決了你的問題,還請點贊支援下。
Demo地址 歡迎轉載,轉載請註明出處:https://juejin.im/post/5a5269a3f265da3e347b15de