項(xiàng)目背景需求:
- 本地創(chuàng)建一個(gè)靜態(tài)庫(kù),靜態(tài)庫(kù)封裝RTC三方庫(kù)的調(diào)用方式以及接收三方庫(kù)的回調(diào)方法。此靜態(tài)庫(kù)與主工程完全獨(dú)立開,主工程可以通過pods集成,也可以通過本地關(guān)聯(lián)進(jìn)行開發(fā)。
- 將此靜態(tài)庫(kù)上傳到公司GitLab倉(cāng)庫(kù)
- 其他成員可以git克隆代碼,交付團(tuán)隊(duì)也可以pods引入。
整個(gè)過程可以分為創(chuàng)建靜態(tài)庫(kù)、配置Pod并上傳Git、使用私有庫(kù)三個(gè)主要階段。
??? 第一步:創(chuàng)建靜態(tài)庫(kù)項(xiàng)目
首先,我們需要在Xcode中創(chuàng)建一個(gè)靜態(tài)庫(kù)項(xiàng)目并編寫代碼。
新建項(xiàng)目:打開Xcode,選擇 File → New → Project,然后選擇 Framework & Library 下的 Static Library,語言選擇 Objective-C。
編寫代碼:在項(xiàng)目中添加或修改你的類。例如,創(chuàng)建一個(gè)
MyCalculator類,對(duì)外提供簡(jiǎn)單的加法功能。
// MyCalculator.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MyCalculator : NSObject
- (NSInteger)addA:(NSInteger)a toB:(NSInteger)b;
@end
NS_ASSUME_NONNULL_END
// MyCalculator.m
#import "MyCalculator.h"
@implementation MyCalculator
- (NSInteger)addA:(NSInteger)a toB:(NSInteger)b {
return a + b;
}
@end
配置項(xiàng)目:在 Build Settings 中,確保
Architectures設(shè)置正確(通常包括arm64,x86_64),以便庫(kù)能同時(shí)支持真機(jī)和模擬器。Build Active Architecture Only在Debug模式下設(shè)為YES可以加快編譯速度,Release模式下設(shè)為NO以構(gòu)建全架構(gòu)版本。暴露公共頭文件:在 Build Phases 的
Copy Files中,添加需要暴露給使用者的頭文件(如MyCalculator.h),目標(biāo)選擇Public Headers并設(shè)置好路徑。設(shè)置為靜態(tài)庫(kù):在 Build Settings 中,設(shè)置
Mach-O Type為 Static Library 。Dynamic 表示動(dòng)態(tài)庫(kù)。
?? 第二步:配置CocoaPods并上傳至Git
這是最關(guān)鍵的一步,我們需要?jiǎng)?chuàng)建podspec文件,并將代碼與描述文件分別管理。
- 創(chuàng)建Podspec文件:在靜態(tài)庫(kù)項(xiàng)目的根目錄下,打開終端,運(yùn)行以下命令生成一個(gè)模板文件:
pod spec create MyStaticLibrary
- 編輯Podspec文件:使用文本編輯器打開該文件,根據(jù)你的項(xiàng)目修改關(guān)鍵配置。一個(gè)精簡(jiǎn)且完整的示例如下:
Pod::Spec.new do |s|
# 基礎(chǔ)信息
s.name = 'MyStaticLibrary' # 庫(kù)的名稱
s.version = '0.1.0' # 版本號(hào),必須與git tag一致
s.summary = '一個(gè)簡(jiǎn)單的計(jì)算器靜態(tài)庫(kù)' # 簡(jiǎn)短描述
s.description = '這是一個(gè)使用Objective-C編寫的靜態(tài)庫(kù),提供了基礎(chǔ)的加法運(yùn)算功能,用于演示私有庫(kù)的創(chuàng)建流程。'
s.homepage = 'https://github.com/your-username/MyStaticLibrary' # 倉(cāng)庫(kù)主頁(yè)
s.license = { :type => 'MIT', :file => 'LICENSE' } # 開源協(xié)議,通常附帶LICENSE文件
s.author = { 'Your Name' => '[email protected]' } # 作者信息
s.social_media_url = 'https://twitter.com/your-username' # 可選
# 平臺(tái)信息
s.platform = :ios, '9.0' # 最低支持版本
s.requires_arc = true # 是否使用ARC
# 源代碼配置(核心)
s.source = { :git => 'https://github.com/your-username/MyStaticLibrary.git', :tag => "#{s.version}" } # 代碼倉(cāng)庫(kù)地址
# 源文件路徑配置
# 如果你的所有代碼都在一個(gè)文件夾下,可以這樣寫
s.source_files = 'MyStaticLibrary/**/*.{h,m}'
# 如果有公開的頭文件需要單獨(dú)指定,可以用public_header_files
# s.public_header_files = 'MyStaticLibrary/**/*.h'
# 如果你的庫(kù)依賴了其他第三方庫(kù),在這里聲明
# s.dependency 'AFNetworking', '~> 4.0'
# 如果你的庫(kù)包含了資源文件(如xib、圖片等),需要這樣配置
# s.resource_bundles = {
# 'MyStaticLibrary' => ['MyStaticLibrary/Assets/*.png']
# }
end
- 上傳代碼到Git倉(cāng)庫(kù):
- 在GitHub、GitLab或碼云上創(chuàng)建一個(gè)新的倉(cāng)庫(kù)(注意:這個(gè)倉(cāng)庫(kù)用來存放你的源代碼,可以設(shè)為私有)。
- 在本地項(xiàng)目根目錄下,初始化Git并提交代碼:
git init
git add .
git commit -m "Initial commit with library source"
git remote add origin https://github.com/your-username/MyStaticLibrary.git
git push -u origin main
- 打上版本標(biāo)簽:這一步至關(guān)重要,CocoaPods通過標(biāo)簽來定位特定版本的代碼。
git tag 0.1.0
git push --tags
- 驗(yàn)證Podspec文件:
在終端中,確保你仍在項(xiàng)目根目錄,運(yùn)行以下命令來驗(yàn)證你的.podspec文件配置是否正確。
pod lib lint
如果驗(yàn)證成功,你會(huì)看到 -> MyStaticLibrary (0.1.0) 和 MyStaticLibrary passed validation. 的提示
第三步:讓其他端可以通過Pods拉取
完成了上面的步驟,你的私有庫(kù)已經(jīng)準(zhǔn)備好被其他人使用了。
方法一:直接指定Git地址(最簡(jiǎn)單)
對(duì)于私有庫(kù),最直接的方式是在使用方的Podfile中直接指定git地址。這是最靈活且推薦的方式。
# 使用方的 Podfile
platform :ios, '9.0'
target 'YourAppTarget' do
use_frameworks! # 如果項(xiàng)目中使用了Swift或者需要framework,則開啟
# 直接指定私有庫(kù)的Git倉(cāng)庫(kù)地址和版本
pod 'MyStaticLibrary', :git => 'https://github.com/your-username/MyStaticLibrary.git', :tag => '0.1.0'
# 或者使用 :branch => 'main' 來跟蹤某個(gè)分支
# 也可以同時(shí)使用其他公開庫(kù)
# pod 'AFNetworking'
end
然后在終端運(yùn)行pod install即可拉取并集成你的靜態(tài)庫(kù)。
方法二:clone代碼到本地
對(duì)于團(tuán)隊(duì)間的共同開發(fā),需要clone下原始代碼,方便團(tuán)隊(duì)間一起協(xié)作開發(fā),此庫(kù)大家都有可能去修改。當(dāng)將代碼克隆下來后,需要將此庫(kù)引入到主工程中。
- 點(diǎn)擊Xcode的左下角
+號(hào)按鈕,Add Files to 你的工程名,找到你剛clone下來的靜態(tài)庫(kù)工程,選擇xxx.xcodeproj。這樣就可以在主工程下看到你的開發(fā)靜態(tài)庫(kù)。
image.png - 點(diǎn)擊主工程,在Target下找到
General設(shè)置項(xiàng),在Frameworks,Libraries,and Embedded Content下,點(diǎn)擊添加按鈕,輸入MyStaticLibrary,找到此庫(kù),點(diǎn)擊添加,并設(shè)置為Do Not Embed
靜態(tài)庫(kù)工程點(diǎn)擊運(yùn)行后,找不到.framework
這是因?yàn)?Apple 為了簡(jiǎn)化項(xiàng)目導(dǎo)航,默認(rèn)隱藏了編譯產(chǎn)物文件夾。我們有兩種辦法可以找到它,你可以根據(jù)自己的習(xí)慣來選擇。
?? 方法一:最簡(jiǎn)單直接——通過菜單找到它
這是最快速的方法,無需任何配置,可以直接定位到你的靜態(tài)庫(kù)文件。
在 Xcode 中,點(diǎn)擊菜單欄的
Product。按住鍵盤上的
Option鍵(也叫 Alt 鍵)。這時(shí)菜單里的
Clean Build Folder會(huì)變成Show Build Folder in Finder,點(diǎn)擊它。Finder 會(huì)直接打開你項(xiàng)目的編譯文件目錄。你需要的靜態(tài)庫(kù)(
.a文件)通常位于Products文件夾下的Debug-iphoneos(真機(jī))或Debug-iphonesimulator(模擬器)文件夾中。
??? 方法二:如果你希望恢復(fù)“Product”文件夾
如果你還是更習(xí)慣在 Xcode 左側(cè)看到“Product”文件夾,可以通過一個(gè)小技巧把它找回來。
在 Finder 中,找到你的工程文件(
.xcodeproj),右鍵點(diǎn)擊它,選擇 “顯示包內(nèi)容”。在打開的文件夾里,找到
project.pbxproj文件,用文本編輯器打開它。搜索
productRefGroup這個(gè)詞。-
你會(huì)看到類似
productRefGroup = ___的一行代碼。將它等號(hào)后面的值,替換為它上面mainGroup =后面的那一長(zhǎng)串值。修改前:
mainGroup = 1234567890ABCDEF; ... productRefGroup = ;修改后:
mainGroup = 1234567890ABCDEF; ... productRefGroup = 1234567890ABCDEF;
保存文件,然后重新打開你的 Xcode 項(xiàng)目。你會(huì)發(fā)現(xiàn)“Product”文件夾又回來了!
靜態(tài)庫(kù)里集成動(dòng)態(tài)庫(kù)
??問題現(xiàn)象:靜態(tài)庫(kù)里集成了動(dòng)態(tài)庫(kù),編譯生成的framework包里面還包含了framework文件夾。
這個(gè)現(xiàn)象確實(shí)是靜態(tài)庫(kù)(.a 或靜態(tài) .framework)在打包時(shí)的一個(gè)常見問題。當(dāng)你的靜態(tài)庫(kù)依賴了外部的動(dòng)態(tài)庫(kù)(.framework 或 .dylib),并且構(gòu)建配置不正確時(shí),Xcode 可能會(huì)錯(cuò)誤地將這些依賴的動(dòng)態(tài)庫(kù)原樣打包進(jìn)你生成的靜態(tài)庫(kù)包體內(nèi),形成了“包里套包”的結(jié)構(gòu)。
核心原因在于 iOS 系統(tǒng)的安全機(jī)制和靜態(tài)庫(kù)的本質(zhì):
-
iOS 不允許嵌套動(dòng)態(tài)庫(kù):iOS 有嚴(yán)格的安全限制,一個(gè)
.framework包內(nèi)不能嵌套包含另一個(gè)動(dòng)態(tài)庫(kù) 。如果你的靜態(tài)庫(kù)工程配置不當(dāng),Xcode 在編譯時(shí)試圖將依賴的動(dòng)態(tài)庫(kù)“復(fù)制”進(jìn)產(chǎn)物中,就會(huì)產(chǎn)生你看到的“包含 framework 文件夾”的無效結(jié)構(gòu)。 -
靜態(tài)庫(kù) vs 動(dòng)態(tài)庫(kù):你自己創(chuàng)建的
.framework通常是靜態(tài)庫(kù)(Mach-O Type 為 Static Library)。它本質(zhì)上是一個(gè)“打包好的文件夾”,包含了二進(jìn)制文件和頭文件。它不應(yīng)該、也無法“容納”另一個(gè)動(dòng)態(tài)庫(kù)并使其正常工作。
? 解決方案:明確“不嵌入”依賴的動(dòng)態(tài)庫(kù)
要解決這個(gè)問題,核心是讓 Xcode 明確:依賴的動(dòng)態(tài)庫(kù)屬于最終 App,而不屬于你這個(gè)靜態(tài)庫(kù)包本身。 你需要將依賴關(guān)系改為“鏈接但不嵌入”。
具體操作步驟如下:
打開你的靜態(tài)庫(kù)工程,在 Xcode 左側(cè)選中你的靜態(tài)庫(kù) Target。
點(diǎn)擊上方的
Build Phases標(biāo)簽頁(yè)。展開
Link Binary With Libraries階段。找到你集成的那個(gè)動(dòng)態(tài)庫(kù)(
.framework)。在 Xcode 較新的版本中(特別是針對(duì)動(dòng)態(tài)庫(kù)),你還需要檢查
General標(biāo)簽頁(yè)。點(diǎn)擊你的靜態(tài)庫(kù) Target,然后在General -> Frameworks, Libraries, and Embedded Content中找到這個(gè)動(dòng)態(tài)庫(kù)。-
最關(guān)鍵的一步:將它右側(cè)的
Embed選項(xiàng)設(shè)置為Do Not Embed。Embed & Sign/Embed Without Signing:表示將這個(gè)庫(kù)復(fù)制到最終產(chǎn)品的包體(.app)里。對(duì)于你正在構(gòu)建的靜態(tài)庫(kù)來說,這是錯(cuò)誤的,因?yàn)樗鼤?huì)導(dǎo)致庫(kù)被錯(cuò)誤地打包進(jìn)靜態(tài)庫(kù)內(nèi)部 。Do Not Embed:表示只鏈接,不復(fù)制。這正是靜態(tài)庫(kù)所需要的——告訴 Xcode:“這個(gè)庫(kù)是我的依賴,編譯時(shí)需要鏈接它的符號(hào),但請(qǐng)不要把它打包進(jìn)我的.framework里,最終 App 會(huì)負(fù)責(zé)包含它?!?/p>
