1. 程式人生 > >iOS元件化解決方案

iOS元件化解決方案

點選上方“iOS開發”,選擇“置頂公眾號”

關鍵時刻,第一時間送達!

640?640?wx_fmt=gif

    先不說楚楓的這般年紀,能夠踏入元武一重說明了什麼,最主要的是,楚楓在剛剛踏入核心地帶時,明明只是靈武七重,而在這兩個月不到的時間,連跳兩重修為,又跳過一個大境界,踏入了元武一重,這般進步速度,簡直堪稱變態啊。

    “這楚楓不簡單,原來是一位天才,若是讓他繼續成長下去,絕對能成為一號人物,不過可惜,他太狂妄了,竟與龔師兄定下生死約戰,一年時間,他再厲害也無法戰勝龔師兄。”有人認識到楚楓的潛力後,為楚楓感到惋惜。

    “哼,何須一年,此子今日就必敗,巫九與龔師兄關係甚好,早就看他不順眼了,如今他竟敢登上生死臺挑戰巫九,巫九豈會放過他?”但也有人認為,楚楓今日就已是在劫難逃。

    “何人挑戰老子?”就在這時,又是一聲爆喝響起,而後一道身影自人群之中掠出,最後穩穩的落在了比鬥臺上。

    這位身材瘦弱,身高平平,長得那叫一個猥瑣,金鉤鼻子蛤蟆眼,嘴巴一張牙帶色兒,說話臭氣能傳三十米,他若是當面對誰哈口氣,都能讓那人跪在地上狂嘔不止。

    不過別看這位長得不咋地,他在核心地帶可是鼎鼎有名,劍道盟建立者,青龍榜第九名,正是巫九是也。

    “你就是巫九?”楚楓眼前一亮,第一次發現,世間還有長得如此奇葩的人。

    巫九鼻孔一張,大嘴一咧,拍著那乾癟的肚子,得意洋洋的道:“老子就是巫九,你挑戰老子?”

    “不是挑戰你,是要宰了你。”楚楓冷聲笑道。

    “好,老子滿足你這個心願,長老,拿張生死狀來,老子今日在這裡瞭解了這小子。”巫九扯開嗓子,對著下方吼了一聲。

    如果他對內門長老這麼說話,也就算了,但是敢這麼跟核心長老說話的,他可真是算作膽肥的,就連許多核心弟子,都是倒吸了一口涼氣,心想這楚楓夠狂,想不到這巫九更狂。

    不過最讓人無言的就是,巫九話音落下不久,真有一位核心長老自人群走出,緩緩得來到了比鬥臺上,左手端著筆墨,右手拿著生死狀,來到了巫九的身前。

    “我去,這巫九什麼身份,竟能這般使喚核心長老?”有人吃驚不已,那長老修為不低,乃是元武七重,比巫九還要高兩個層次,但卻這般聽巫九的話,著實讓人吃驚不已。

    “這你就不知道了吧,巫九在前些時日,拜了鍾離長老為師尊,已正式得到鍾離長老的親傳。”有人解釋道。

    “鍾離長老?可是那位性情古怪的鐘離一護?”

    “沒錯,就是他。”

    “天哪,巫九竟然拜入了他的門下?”

    人們再次大吃一驚,那鍾離一護在青龍宗可是赫赫有名,若要是論其個人實力,在青龍宗內絕對能夠排入前三,連護宗六老單打獨鬥都不會是他的對手。

    只不過那鍾離一護,如同諸葛青雲一樣,也是一位客卿長老,所以在青龍宗內只是掛個頭銜,什麼事都不管,更別說傳授宗內弟子技藝了,如今巫九竟然能拜入他老人家門下,著實讓人羨慕不已。

    “恩怨生死臺,的確可以決鬥生死,但必須要有所恩怨,你們兩個人,可有恩怨?”那位長老開口詢問道。

由於近期迭代週期變長,有時間想想程式碼持續改進的問題,再加上各業務模組程式碼從去年雜亂無章的狀態,到目前整體結構基本清晰,進而想到了模組之間解耦的問題,於是有了本文,關於iOS元件化的一些思路及最終的解決方案。

為什麼要元件化

技術界如今已存在很多關於元件化的解決方案,Class-Protocol、Target-Action等等,無論採用哪種方案,大家的目的都是為了解決程式碼龐大到一定規模時,依舊可以比較方便的進行管理和開發。這個時候就需要對各個業務模組進行梳理,在程式碼層面實現高內聚、低耦合,降低它們相互之間的變化帶來的影響,從而提升開發效率。

先來看看如下兩個圖,對比一下:

640?wx_fmt=jpeg

模組間跳轉現狀.jpg

640?wx_fmt=jpeg

中間層框架解耦方案.jpg

從圖中可以看出,在經過中間層框架跳轉分發之後,各業務模組之間不存在引用關係,程式碼相互隔離,呼叫層次清晰,實現了模組間的真正解耦,完美的過渡到元件化的流程。

框架內部的實現原理是什麼

這裡採用的是openURL: 和 openWithMapKey:的兩種呼叫方式,以便實現App之間跳轉及模組之間的跳轉操作,具體採用哪種方式之後會講到,下面來看一下中間層框架 JCNavigator 簡單的呼叫過程:

640?wx_fmt=jpeg

中間層跳轉分發流程.jpg

接下來詳細介紹下流程中出現的方法及相關類:

openURL:

  • 支援App之間的跳轉

    支援設定、電話等系統Apps和info.plist白名單中的第三方Apps跳轉

  • 支援Module之間的跳轉

    傳參時僅支援NSString資料型別的賦值

  • 支援Module內部頁面的跳轉

    傳參時僅支援NSString資料型別的賦值

openWithMapKey:

  • 支援Module內部頁面的跳轉

    傳參時支援NSString、NSArray、UIImage等系統資料型別及自定義資料型別的賦值

  • 支援Module之間的跳轉

    為了模組間的解耦,傳參時建議使用系統資料型別,避免使用自定義資料型別

總結:兩種呼叫方法各有優勢,通過推送、Widget、第三方App等外部入口開啟你的App執行跳轉操作時,建議使用openURL:,其他情況的跳轉都採用openWithMapKey:的呼叫方式。

JCModuleMap

從上圖可以看出,模組間無論是通過 openURL: 還是 openWithMapKey: 方法,都是找到對應的JCModuleMap,然後實現最終的頁面跳轉。接下來看看如何通過JCModuleMap實現這一操作:

  • 子類化

640?wx_fmt=jpeg

JCModuleMap子類化.jpg

//  JCTestModuleMap.m

NSString *const JCFirstLevelMapKey = @"JC_firstLevel";
NSString *const JCSecondLevelMapKey = @"JC_secondLevel";
NSString *const JCThirdLevelMapKey = @"JC_thirdLevel";
NSString *const JCContentDetailMapKey = @"JC_contentDetail";

@implementation JCTestModuleMap

- (NSString *)mapKeyPrefix
{
    return @"JC";
}

- (NSDictionary<NSString *,Class> *)classesForMapKeys
{
    return @{JCFirstLevelMapKey: NSClassFromString(@"JCFirstLevelViewController"),
             JCSecondLevelMapKey: NSClassFromString(@"JCSecondLevelViewController"),
             JCThirdLevelMapKey: NSClassFromString(@"JCThirdLevelViewController"),
             JCContentDetailMapKey: NSClassFromString(@"JCContentDetailViewController"),
             };
}

@end
  • NSURL/mapKey對映原理

640?wx_fmt=jpeg

NSURL:mapKey對映原理.jpg

總結:openURL: 通過NSURL及子類化JCModuleMap中實現的mapKeyPrefix拼接對應的mapKey,然後和 openWithMapKey: 一樣,都是通過classesForMapKeys方法,獲取class-mapKey的對映關係,進而跳轉到module中對應class的檢視控制器頁面。

引數傳遞

  • 申明介面協議,以屬性的方式傳遞:

@protocol JC_contentDetail <NSObject>

@property (nonatomicstrongNSString *currentIndex;
@property (nonatomicstrongNSString *testId;
@property (nonatomicstrongNSArray *testArray;

@end

@interface JCContentDetailViewController : UIViewController<JC_contentDetail>

@end
  • 通過屬性名-屬性值生成字典傳參:

+ (void)openContentDetailViewControllerWithCurrentIndex:(NSString *)currentIndex testId:(NSString *)testId testArray:(NSArray *)testArray
{
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:3];
    if ([currentIndex isKindOfClass:[NSString class]]) {
        params[@"currentIndex"] = currentIndex;
    }
    if ([testId isKindOfClass:[NSString class]]) {
        params[@"testId"] = testId;
    }
    if ([testArray isKindOfClass:[NSArray class]]) {
        params[@"testArray"] = testArray;
    }
    [[JCNavigator sharedNavigator] openWithMapKey:JCContentDetailMapKey propertiesBlock:^NSDictionary *{
        return params;
    } presented:YES animated:YES];
}

頁面展示效果設定

openURL: 時,通過JCModuleMap子類化實現的方法設定:

// 是否模態彈出(預設NO)
- (BOOL)presentedForClass:(Class)viewControllerClass;

// 是否有動畫(預設YES)
- (BOOL)animatedForClass:(Class)viewControllerClass;

openWithMapKey:方法呼叫時直接設定:

// 是否模態及動畫
- (void)openWithMapKey:(NSString *)mapKey propertiesBlock:(JCNavigatorPropertiesBlock)block presented:(BOOL)presented animated:(BOOL)animated;

Modules之間的解耦是怎麼實現的

好了,在實現JCModuleMap子類化及相關跳轉配置之後,現在最關鍵的操作來了,如何為各個modules之間提供通訊及呼叫的介面,才能最大限度的解決解耦的問題?廢話不多說,先來看看這段程式碼:

//  JCNavigator+JCTestModuleInterface.h

@interface JCNavigator (JCTestModuleInterface)

+ (void)openFirstLevelViewController;

+ (void)openSecondLevelViewController;

@end

//  JCNavigator+JCTestModuleInterface.m

@implementation JCNavigator (JCTestModuleInterface)

+ (void)load
{
    [[JCNavigator sharedNavigator] addModuleMap:[JCTestModuleMap new]];
}

+ (void)openFirstLevelViewController
{
    [[JCNavigator sharedNavigator] openWithMapKey:JCFirstLevelMapKey];
}

+ (void)openSecondLevelViewController
{
    [[JCNavigator sharedNavigator] openWithMapKey:JCSecondLevelMapKey];
}

@end
  • 從程式碼可以看出:

1)每個module宣告並實現對應的JCNavigator類別;

2)在JCNavigator類別中實現load類方法,新增對應子類化JCModuleMap物件;

3)JCNavigator類別標頭檔案中提供統一的對外介面,實現檔案中封裝內部呼叫細節,從而解決modules之間的耦合問題。

  • 下圖概述了實現過程:

640?wx_fmt=jpeg

Modules介面服務.jpg

總結

對於元件化的介紹,到這裡接近尾聲,整個解決方案有優點也有缺點,需要開發者各自權衡:

  • 優點

1)支援Apps之間跳轉;

2)支援Modules之間跳轉及通訊(引數傳遞);

3)所有跳轉只基於JCNavigator中間層框架;

4)可實現Modules之間解耦、互不依賴;

5)無需額外處理Modules頁面間的層級關係。

  • 缺點

1)需要子類化JCModuleMap,並將例項化物件新增到JCNavigator,增加了記憶體消耗;

2)如果viewController類名或傳遞的引數發生改變,Xcode不會報錯也沒有警告,需及時維護子類化JCModuleMap的實現,並更新JCNavigator類別中的呼叫程式碼。

關於框架的更多實現細節,請關注github開原始碼

JCNavigator。https://github.com/imjoych/JCNavigator

640.jpeg

  • https://mp.weixin.qq.com/s/z3tqvZkgu1EjI2Hj9AU8YQ

  • iOS開發整理髮布,轉載請聯絡作者獲得授權