組件逆航、模塊的區(qū)別和聯(lián)系
組件:提供特定單一功能或則通用可復(fù)用UI;把一些可復(fù)用的UI元素或則單一功能渔肩,抽離為單個組件因俐,便于項目的維護和開發(fā)。多個組件可以組合成組件庫赖瞒,方便調(diào)用和復(fù)用女揭,組件間也可以嵌套,小組件組合成大組件
模塊(模塊組件):分屬同一業(yè)務(wù)的代碼進行隔離(分裝)成獨立的模塊栏饮,可以獨立運行吧兔,以頁面、功能或其他不同粒度劃分程度不同的模塊袍嬉,位于業(yè)務(wù)框架層境蔼,模塊間通過接口調(diào)用灶平,目的是降低模塊間的耦合;把一些可復(fù)用的代碼箍土,抽離為單個模塊逢享,便于項目的維護和開發(fā)∥庠澹可以調(diào)用組件來組成模塊瞒爬,多個模塊可以組合成業(yè)務(wù)框架。
業(yè)務(wù)線模塊(業(yè)務(wù)框架層):該業(yè)務(wù)線下關(guān)聯(lián)的多個模塊組件的框架層(虛擬的層)
為什么使用組件化和模塊化沟堡?
開發(fā)和調(diào)試效率高:隨著功能越來越多侧但,代碼結(jié)構(gòu)會越發(fā)復(fù)雜,要修改某一個小功能航罗,可能要重新翻閱整個項目的代碼禀横,把所有相同的地方都修改一遍,重復(fù)勞動浪費時間和人力粥血,效率低柏锄;使用組件化,每個相同的功能結(jié)構(gòu)都調(diào)用同一個組件复亏,只需要修改這個組件趾娃,即可全局修改。
可維護性強:便于后期代碼查找和維護蜓耻。
避免阻斷:模塊化是可以獨立運行的茫舶,如果一個模塊產(chǎn)生了bug,不會影響其他模塊的調(diào)用刹淌。
版本管理更容易:如果由多人協(xié)作開發(fā)饶氏,可以避免代碼覆蓋和沖突。
組件-模塊關(guān)系示意圖:
制作私有pod組件的過程
組件創(chuàng)建步驟
官方示例:https://code.tutsplus.com/tutorials/creating-your-first-cocoapod--cms-24332
參考文獻:http://www.reibang.com/p/7b4667cde80b
1有勾、開個遠程代碼托管庫疹启,庫地址未url
2、git clone url 到本地文件目錄下path
3蔼卡、在path目錄下喊崖,執(zhí)行pod lib create xxxx,創(chuàng)建pod模版工程
4、在pod模版目錄文件中雇逞,找到xxx.podspec文件所在目錄荤懂,修改配置下xxx.podspec文件(也可以不配置,等最后版本提交時再修改配置); 在和xxx.podspec文件同一層級目錄下塘砸,有個xxx目錄节仿,這個目錄中包含/Classes(存放.h、.m文件)和/Assets(存放圖片掉蔬、xib廊宪、音視頻等資源矾瘾,這些資源會打包在xxx.framework中;和xxx.podspec文件同目錄下箭启,有個/Example目錄壕翩,打開xxx.xcworkspace工程,在Pods工程下xxx文件目錄下傅寡,創(chuàng)建工程代碼放妈,每有了代碼改動尤其是類的增加/減少、資源文件增減都要對/Example目錄執(zhí)行下pod install荐操,不然大猛,xxx.xcworkspace中可能引用不到對應(yīng)新增/減少的類、資源,podspec文件官方詳解
5淀零、每次開發(fā)完,及時將代碼提交到遠程托管庫膛壹;當這個版本的功能開發(fā)完后驾中,修改校驗xxx.podspec文件(發(fā)布版本時,必改項是版本號version),給版本號打tag,必須打tag,因為xxx.podspec中的s.version就是這個tag
?$ git tag -a 0.0.1 -m “V0.0.1”
?$ git push origin version號
?$ git push —tags
6模聋、驗證.podspec是否配置正確
?$ cd podspec文件所在目錄
?$ pod lib lint (驗證)
?$ pod lib lint —verbose (驗證-并顯示詳細信息)
?$ pod lib lint —allow-warnings? (驗證—忽略警告)
?$ pod spec lint podName.podspec —verbose? (這個從本地和遠程驗證你的pod能否通過驗證,上面三個都是從本地驗證你的pod能否通過驗證)
?7肩民、至此一個pod組件制作完畢,但是當有多個pod組件時链方,為便于管理這些組件庫持痰,就創(chuàng)建了Spec Repo,來統(tǒng)一管理多個pod組件的代碼庫地址url祟蚀;
?// 創(chuàng)建Spec Repo遠程私有托管庫
?$ pod repo add REPO_NAME SOURCE_URL
?// 在本地cocoapods目錄下查找REPO_NAME是否創(chuàng)建成功
?$ cd ~/.cocoapods/repos/REPO_NAME
?8工窍、把xxx.podspec添加到Spec Repo中,這樣多個pod組件庫的下載就可以直接訪問Spec Repo來下載對應(yīng)的組件庫了
?$ pod repo push REPO_NAME SPEC_NAME.podspec
?9前酿、后續(xù)每次迭代開發(fā)提交患雏,重復(fù)5、6罢维、8的操作(后續(xù)迭代開發(fā)操作6可以省略)
?10淹仑、隨著版本的迭代,某一個功能可能會拆分成幾個模塊肺孵,這時就會用到subspec,將一個大組件拆分成幾個小的子組件subspec匀借,官網(wǎng)文獻鏈接
資源文件的加載
不管是.a庫,還是framework庫平窘,使用圖片資源吓肋,都可以將資源放進bundle中存放,.a/framework庫文件加載資源從bundle中取圖片初婆;
制作三方庫在考慮Static Library格式的庫庫或者Framework格式的庫時:依賴圖片資源蓬坡,使用Framework格式的庫猿棉;想要擁有完整的依賴關(guān)系,使用Framework格式的庫,此時外界可能需要剔除Framework格式的庫之外的依賴庫屑咳,而采用Framework格式的庫內(nèi)的依賴庫萨赁,否則雖然不會產(chǎn)生依賴沖突,但會增加包大小
制作三方庫或者使用三方庫過程中兆龙,比如A組件和B組件命名了相同名字的類文件杖爽,會出現(xiàn)命名沖突報duplicate symbol錯誤的問題,對于制作framework或則.a組件時紫皇,命名規(guī)范慰安,以庫的前綴為類名,避免重名問題聪铺;
符號沖突文獻:
http://www.reibang.com/p/9cf63c30f650
http://www.reibang.com/p/2591c0ec07f2
幾種三方庫制作過程mach -O Type選型區(qū)別:(build settings -> linking ->mach -O Type)
Executable: `靜態(tài)庫`化焕,輸出二進制
Dynamic Library:`動態(tài)非共享庫`,輸出動態(tài)鏈接庫非共享庫铃剔,程序`運行`時鏈接到`內(nèi)存`撒桨,大部分場景下不可共享;app extension键兜、部分macOS場景下可以共享
Bundle:`動態(tài)非共享庫`凤类,和Dynamic Library相近,不過需要手動調(diào)用函數(shù)加載
Static Library:? `靜態(tài)庫`普气,輸出靜態(tài)鏈接庫谜疤,程序`編譯`時拷貝到`內(nèi)存`
Relocatable Object File:`靜態(tài)庫`,和Static Library類似现诀,但體積更小
cocopods官網(wǎng)說明資源加載有兩種方式:
一夷磕、spec.resources、spec.resource,這種方式仔沿,資源文件會直接放置在組件所在的framework中(如下面的ResourceStyleSpec.framework)企锌,
資源路徑查找方式:
1)獲取當前組件的framework(也是一種bundle)的方式為:
NSBundle *bundle = [NSBundle bundleForClass:[self class]]
2)查找具體資源路徑:
NSString *xxxPath= [bundle pathForResource:@"xxx" ofType:@"xib/png/..."];
二、spec.resource_bundles于未、?spec.resource_bundle撕攒,這種方式,資源文件會放置在組件所在的framework下的專門的資源bundle中(如下面的ResourceStyleSpec.framework下的ResourceStyleSpec.bundle)
資源路徑查找方式:
1)獲取當前組件XXX的framework(也是一種bundle)的方式為:
NSBundle *bundle = [NSBundle bundleForClass:[self?class]]
2)獲取當前組件的framework下的資源bundle:XXX.bundle:
NSBundle *sourceBundle = [NSBundle bundleWithPath:[bundle ?pathForResource:@"XXX" ofType:@"bundle"]];?
3)查找具體資源路徑:
NSString *xxxPath= [sourceBundle pathForResource:@"xxx" ofType:@"xib/png/..."];
相關(guān)資料:
http://www.reibang.com/p/871e1b8891d8
組件的通信
一烘浦、組件的業(yè)務(wù)粒度劃分
關(guān)于組件業(yè)務(wù)粒度劃分抖坪,沒有統(tǒng)一的標準,根據(jù)實際業(yè)務(wù)來界定組件提供功能服務(wù)的邊界闷叉;比如支付組件擦俐,就是提供一個完整的支付流程的組件,外界只需要集成該組件握侧,通過調(diào)用簡單的組件提供的API就可以完成支付業(yè)務(wù)蚯瞧。
二嘿期、組件依賴方式確定
未組件化時,各業(yè)務(wù)模塊耦合在一起的模塊依賴關(guān)系:
通過上面三種方案的對比备徐,可以看到:
業(yè)務(wù)組件和中間調(diào)度模塊相互依賴時,中間調(diào)度模塊依賴業(yè)務(wù)組件甚颂,當依賴了中間調(diào)度模塊蜜猾,也間接依賴了業(yè)務(wù)組件,并沒有達到解耦的目的振诬,是不合理的蹭睡;因此,為了達到真正的解耦赶么,應(yīng)該是業(yè)務(wù)組件依賴中間調(diào)度模塊肩豁,業(yè)務(wù)組件需要對外開發(fā)的實體,注冊到中間調(diào)度模塊中辫呻,外部通過調(diào)用中間調(diào)度模塊來獲取業(yè)務(wù)組件實體蓖救,外部并不依賴業(yè)務(wù)組件,從而達到解耦的目的印屁,即依賴關(guān)系采用第三種-業(yè)務(wù)組件單向依賴中間調(diào)度模塊的方案
三、組件間通訊技術(shù)方案實現(xiàn)
目前比較流行的組件間通訊技術(shù)方案有三種:scheme方式斩例、protocol方式雄人、target_action方式。三種方式都能實現(xiàn)組件間通訊念赶,其中scheme方式和protocol方式都必須在運行內(nèi)存中維護一個注冊表础钠,target_action方式不必維護這個注冊表(當然使用者按需求想維護一個也是可以支持的),因此叉谜,從解耦的徹底性來看旗吁,target_action方式解耦更為徹底,但是組件間的通訊作為一個支撐所有場景下的組件間通訊方案停局,解耦只是其中一個的量化指標很钓,組件通訊代碼和組件調(diào)用鏈路代碼可維護性和易接入性等也是量化指標,個人使用這三種方式的過程中董栽,總結(jié)了這三種方式的選型場景码倦,選擇組件化方案時,可以適度參考一下幾點:
1锭碳、純iOS端組件間調(diào)用袁稽,使用protocol,可以傳圖片UIImage對象等非基礎(chǔ)數(shù)據(jù)類型參數(shù);但不能跨端使用
2擒抛、scheme方式場景一:系統(tǒng)通用跨端能力實現(xiàn)推汽,比如短信鏈接补疑、端外push、h5歹撒、瀏覽器莲组、其他APP等跨端跳轉(zhuǎn)到APP的某一個頁面,就必須使用scheme方式栈妆,采用openUrl方式打開胁编;
scheme方式場景二:APP內(nèi)部組件間調(diào)用和組件內(nèi)部調(diào)用使用scheme方式也是scheme的領(lǐng)一種使用方式
3、target_action方式鳞尔,解耦比較徹底嬉橙,完全利用系統(tǒng)的消息發(fā)送機制,不需要維護一個注冊表,但也導(dǎo)致了一些特殊場景寥假,比如需要拿到先前創(chuàng)建過的實例進行跳轉(zhuǎn)市框,雖然CTMediator組件中提供了cachedTarget來緩存實例,但是這個實例是第一次調(diào)用CTMediator后才存儲的糕韧,實際場景這個實例是外界創(chuàng)建的枫振,這樣使用CTMediator就拿不到這個外界創(chuàng)建的實例了。如果?CTMediator也維護一個外界注冊的實例表萤彩,和protocol和scheme的方式差異也不大了
作為一個APP粪滤,尤其一個大型的項目APP,除了APP內(nèi)部通訊雀扶,跨端通訊能力也是必須的杖小,因此,不管是采用protocol還是使用target_action方式愚墓,由于牽涉到跨端通訊予权,因為都會使用系統(tǒng)通用跨端通信能力,即scheme的方式場景一浪册。因為項目中扫腺,組件通訊方案都是protocol或者target_action方式結(jié)合scheme方式使用的,即在需要用到scheme的解析的case村象,根據(jù)約定的scheme字段笆环,使用對應(yīng)約定的protocol或則arget_actio去做組件間的通訊如跳轉(zhuǎn);用不到scheme的就按照protocol或者target_action方式進行組件間通訊。