這篇文章主要給大家介紹了關於iOS組件依賴避免衝突的小技巧,文中通過示例代碼介紹的非常詳細,對各位iOS開發者們具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
問題緣由
本文以 YBImageBrowser[1] 組件舉例。
YBImageBrowser 依賴了 SDWebImage,在使用 CocoaPods 集成到項目中時,可能會出現一些依賴衝突的問題,最近社區提了多個 Issues 並且在 Insights -> Traffic -> Popular content 中看到了此類問題很高的關注度,所以不得不着手解決。
嚴格的版本限制
一個開源組件的迭代過程中,保證上層接口的向下兼容就不錯了。爲了優化性能並且控制內存,YBImageBrowser 沒有直接用其最上層的接口,而是單獨使用了下載模塊和緩存模塊,SDWebImage 的迭代升級很容易導致筆者的組件兼容不了,所以之前一直是類似這樣依賴的:
s.dependency 'SDWebImage', '~> 5.0.0'
這樣做的好處是限制足夠小版本範圍,降低 SDWebImage 接口變動導致組件代碼錯誤的風險。但如果 SDWebImage 升級到 5.1.0,不管相關 API 是否變動,CocoaPods 都視爲依賴衝突。
其它組件依賴了不同版本的 SDWebImage
當兩個組件依賴了同一個組件的不同版本,並且依賴的版本沒有交集,比如:
A.dependency 'SDWebImage', '~> 4.0.0' B.dependency 'SDWebImage', '~> 5.0.0'
那麼 A 和 B 同時集成進項目會出現依賴衝突。
解決方案
使用 CocoaPods 集成項目非常便捷,對於組件使用者來說,總是想在任何場景下都能輕易集成,並且能在將來享受組件的更新優化,顯然前面提到的問題可能會影響集成的便捷性。
更模糊的版本限制
很多時候一個大版本的組件不會改動 API,並且對於社區流行的組件我們可以寄一定希望於其做好向下兼容,所以放寬依賴的版本限制能覆蓋將來更多的版本(規則參考:podspec dependency[2]):
s.dependency 'SDWebImage', '>= 5.0.0'
爲什麼不乾脆去掉版本限制呢?
因爲 YBImageBrowser 3.x 是基於 SDWebImage 5.0.0 開發的,筆者可以明確不兼容 5.0.0 之前的版本,所以在 SDWebImage 將來迭代版本出現相關 API 不兼容之前,這個限制都是“完美”覆蓋所有版本的。
避免依賴衝突的暴力方案
當有其它組件依賴了不同版本的 SDWebImage,粗暴的解決方案如下:
• 直接修改其它組件依賴的 SDWebImage 版本。
• 將 YBImageBrowser 手動導入項目,並且修改代碼去適應當前的 SDWebImage 版本。
• 社區朋友一個 Issue 中提到的方法:在 ~/.cocoapods/repos 目錄下找到 YBImageBrowser 文件夾,更改對應版本的 podspec.json 文件裏對 SDWebImage 的依賴版本。
顯然,上面的幾種方案不太優雅,手動導入項目難以享受組件的更新優化,修改本地 repo 信息會因爲 repo 列表的更新而復位。
避免依賴衝突的優雅方案
出現依賴衝突是必須要解決的問題,其它組件依賴的版本限制可以視爲不變量,解決方案可以從組件的製作方面考慮。
要做到的目標是,既滿足部分用戶快速集成組件,又能讓部分用戶解決依賴衝突的前提下保證能享受組件將來的更新優化。
答案就是subspec,以下是 YBImageBrowser.podspec 部分代碼(完整代碼[3]):
s.subspec "Core" do |core| core.source_files = "YBImageBrowser/**/*.{h,m}" core.dependency 'SDWebImage', '>= 5.0.0' end s.subspec "NOSD" do |core| core.source_files = "YBImageBrowser/**/*.{h,m}" core.exclude_files = "YBImageBrowser/WebImageMediator/YBIBDefaultWebImageMediator.{h,m}" end
由此,用戶可以自由的選擇是否需要依賴 SDWebImage,在 Podfile 裏的觀感大致是這樣:
// 依賴 SDWebImage pod 'YBImageBrowser' // 不依賴 SDWebImage pod 'YBImageBrowser/NOSD'
那麼在 YBImageBrowser 代碼中應該如何區分是否依賴了 SDWebImage 並且提供默認實現呢?
第一步是設計一個抽象接口(這個接口不依賴 SDWebImage):
@protocol YBIBWebImageMediator <NSObject> // Download methode, caching methode, and so on. @end
第二步是在YBImageBrowser.h中定義一個遵循該接口的屬性:
/// 圖片下載緩存相關的中介者(賦值可自定義) @property (nonatomic, strong) id<YBIBWebImageMediator> webImageMediator;
第三步是實現一個默認的中介者(這個類依賴了 SDWebImage):
@interface YBIBDefaultWebImageMediator : NSObject <YBIBWebImageMediator> @end @implementation YBIBDefaultWebImageMediator //通過 SDWebImage 的 API 實現 <YBIBWebImageMediator> 協議方法 @end
第四步是在內部代碼中通過條件編譯導入並初始化默認中介者:
#if __has_include("YBIBDefaultWebImageMediator.h") #import "YBIBDefaultWebImageMediator.h" #endif ... #if __has_include("YBIBDefaultWebImageMediator.h") _webImageMediator = [YBIBDefaultWebImageMediator new]; #endif
第五步在 YBImageBrowser.podspec 中也可以看到,在不依賴 SDWebImage 的集成方式時排除了兩個文件:YBIBDefaultWebImageMediator.{h.m}。
由此便實現了目標:
• 用依賴 SDWebImage 的集成方式快速集成。
• 使用不依賴 SDWebImage 的集成方式避免各種情況下的依賴衝突,但注意這種情況需要自行實現一個遵循<YBIBWebImageMediator>協議的中介者。
以上便是避免依賴衝突的小技巧,希望讀者朋友能提出更好的建議或意見😁。
參考
[1]https://github.com/indulgeIn/YBImageBrowser
[2]https://guides.cocoapods.org/syntax/podspec.html#dependency
[3]https://github.com/indulgeIn/YBImageBrowser/blob/master/YBImageBrowser.podspec
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對神馬文庫的支持。