MGJRouter程式碼分析
今天來聊聊元件化,之前一直聽說大廠在搞,什麼淘寶架構,什麼蘑菇街,既然談到了架構的問題,那必屬重中之重。接下來分析一下蘑菇街開源的程式碼,自己做個總結。
引入
類書本的文章個人感覺還是寫不來的,再搬到自己寫的東西這來也不合適,所以直接上一連結,通過連結文章大致可瞭解下它的前身後世,產生原因,以及整體巨集觀架構設計,而我接下來要做的是細化,以及轉化,便於自己吸收 ---------> 元件化架構漫談
1. 話不多說,先看入口:
@interface MGJRouter ()
/**
* 儲存了所有已註冊的 URL
* 結構類似 @{@"beauty": @{@":id": {@"_", [block copy]}}}
*/
@property (nonatomic) NSMutableDictionary *routes;
@end
+ (instancetype)sharedInstance
{
static MGJRouter *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
很明顯,蘑菇街架構(以下簡稱MGJ)通過該單例作管理,統一進行調配,而該單例僅有一個變數,就是routes,實際上它僅僅是管理了一個字典的結構,具體字典內有哪些內容,我們慢慢看;
2. 回撥Block的定義
/**
* routerParameters 裡內建的幾個引數會用到上面定義的 string
*/
typedef void (^MGJRouterHandler)(NSDictionary *routerParameters);
/**
* 需要返回一個 object,配合 objectForURL: 使用
*/
typedef id (^MGJRouterObjectHandler)(NSDictionary *routerParameters);
上面這兩個block定義是MGJ註冊URL的回撥,一個帶返回值,另一個不帶,在這裡我們說一下帶返回值的block用法;如下舉例 ------>
//宣告
typedef UIViewController *(^ViewControllerHandler)();
//作引數
@interface DemoListViewController : UIViewController
+ (void)registerWithTitle:(NSString *)title handler:(ViewControllerHandler)handler;
@end
//定義
@implementation DemoListViewController
+ (void)registerWithTitle:(NSString *)title handler:(ViewControllerHandler)handler
{
UIViewController* vc = handler()
}
@end
//在別處呼叫
@implementation DemoDetailViewController
[DemoListViewController registerWithTitle:@"基本使用" handler:^UIViewController *{
return DemoDetailViewController();
}];
@end
如上,我們把ViewControllerHandler
的執行延遲到了實際呼叫的時刻,並且我們可以在這個handler的實現中帶入很多資訊;
3. MGJ資料結構管理
extern NSString *const MGJRouterParameterURL;
extern NSString *const MGJRouterParameterCompletion;
extern NSString *const MGJRouterParameterUserInfo;
//*************************************************
static NSString * const MGJ_ROUTER_WILDCARD_CHARACTER = @"~"; //這是一個佔位符
static NSString *specialCharacters = @"/?&.";
NSString *const MGJRouterParameterURL = @"MGJRouterParameterURL";
NSString *const MGJRouterParameterCompletion = @"MGJRouterParameterCompletion";
NSString *const MGJRouterParameterUserInfo = @"MGJRouterParameterUserInfo";
從這裡我們可以看出,MGJ的路由管理,實際上是一個解析url以及對應的管理,我們舉幾個URL來看一下:
@"mgj://"
@"mgj://foo/bar/none/exists"
@"mgj://foo/bar"
@"mgj://category/家居"
@"mgj://category/travel"
@"mgj://search/:query"
@"mgj://detail"
@"mgj://search/:keyword"
@"mgj://search_top_bar"
通過上面的URL我們可以看出,路由的管理實際上就是url的解析過程,下面我們來具體看一下解析過程;
4. URL解析
- route url
- (NSArray*)pathComponentsFromURL:(NSString*)URL
{
NSMutableArray *pathComponents = [NSMutableArray array];
if ([URL rangeOfString:@"://"].location != NSNotFound) {
NSArray *pathSegments = [URL componentsSeparatedByString:@"://"];
// 如果 URL 包含協議,那麼把協議作為第一個元素放進去
[pathComponents addObject:pathSegments[0]];
// 如果只有協議,那麼放一個佔位符
URL = pathSegments.lastObject;
if (!URL.length) {
[pathComponents addObject:MGJ_ROUTER_WILDCARD_CHARACTER];
}
}
for (NSString *pathComponent in [[NSURL URLWithString:URL] pathComponents]) {
if ([pathComponent isEqualToString:@"/"]) continue;
if ([[pathComponent substringToIndex:1] isEqualToString:@"?"]) break;
[pathComponents addObject:pathComponent];
}
return [pathComponents copy];
}
-
key-value
- (NSMutableDictionary *)addURLPattern:(NSString *)URLPattern
{
NSArray *pathComponents = [self pathComponentsFromURL:URLPattern];
NSMutableDictionary* subRoutes = self.routes;
for (NSString* pathComponent in pathComponents) {
if (![subRoutes objectForKey:pathComponent]) {
subRoutes[pathComponent] = [[NSMutableDictionary alloc] init];
}
subRoutes = subRoutes[pathComponent];
}
return subRoutes;
}
- 核心url解析
- (NSMutableDictionary *)extractParametersFromURL:(NSString *)url
{
NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
parameters[MGJRouterParameterURL] = url;
NSMutableDictionary* subRoutes = self.routes;
NSArray* pathComponents = [self pathComponentsFromURL:url];
BOOL found = NO;
// borrowed from HHRouter(https://github.com/Huohua/HHRouter)
for (NSString* pathComponent in pathComponents) {
// 對 key 進行排序,這樣可以把 ~ 放到最後
NSArray *subRoutesKeys =[subRoutes.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
return [obj1 compare:obj2];
}];
for (NSString* key in subRoutesKeys) {
if ([key isEqualToString:pathComponent] || [key isEqualToString:MGJ_ROUTER_WILDCARD_CHARACTER]) {
found = YES;
subRoutes = subRoutes[key];
break;
} else if ([key hasPrefix:@":"]) {
found = YES;
subRoutes = subRoutes[key];
NSString *newKey = [key substringFromIndex:1];
NSString *newPathComponent = pathComponent;
// 再做一下特殊處理,比如 :id.html -> :id
if ([self.class checkIfContainsSpecialCharacter:key]) {
NSCharacterSet *specialCharacterSet = [NSCharacterSet characterSetWithCharactersInString:specialCharacters];
NSRange range = [key rangeOfCharacterFromSet:specialCharacterSet];
if (range.location != NSNotFound) {
// 把 pathComponent 後面的部分也去掉
newKey = [newKey substringToIndex:range.location - 1];
NSString *suffixToStrip = [key substringFromIndex:range.location];
newPathComponent = [newPathComponent stringByReplacingOccurrencesOfString:suffixToStrip withString:@""];
}
}
parameters[newKey] = newPathComponent;
break;
}
}
// 如果沒有找到該 pathComponent 對應的 handler,則以上一層的 handler 作為 fallback
if (!found && !subRoutes[@"_"]) {
return nil;
}
}
// Extract Params From Query.
NSArray<NSURLQueryItem *> *queryItems = [[NSURLComponents alloc] initWithURL:[[NSURL alloc] initWithString:url] resolvingAgainstBaseURL:false].queryItems;
for (NSURLQueryItem *item in queryItems) {
parameters[item.name] = item.value;
}
if (subRoutes[@"_"]) {
parameters[@"block"] = [subRoutes[@"_"] copy];
}
return parameters;
}
+ (void)openURL:(NSString *)URL withUserInfo:(NSDictionary *)userInfo completion:(void (^)(id result))completion
{
URL = [URL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary *parameters = [[self sharedInstance] extractParametersFromURL:URL];
[parameters enumerateKeysAndObjectsUsingBlock:^(id key, NSString *obj, BOOL *stop) {
if ([obj isKindOfClass:[NSString class]]) {
parameters[key] = [obj stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
}];
if (parameters) {
MGJRouterHandler handler = parameters[@"block"];
if (completion) {
parameters[MGJRouterParameterCompletion] = completion;
}
if (userInfo) {
parameters[MGJRouterParameterUserInfo] = userInfo;
}
//所以註冊路由時的回撥是在這裡才呼叫到的
//也就是openURL響應了register的回撥
if (handler) {
[parameters removeObjectForKey:@"block"];
handler(parameters);
}
}
}
5. 後續?
相關文章
- 程式碼分析
- AsyncTask 程式碼分析
- 以太坊原始碼分析(5)accounts程式碼分析原始碼
- LNet程式碼分析
- beego框架程式碼分析Go框架
- 藉助ai來分析程式碼,理解程式碼AI
- 簡述元件化解決方案CTMediator與MGJRouter的主要思想元件化
- qt程式建立及模板程式碼分析QT
- 搭建sonarqube分析golang程式碼Golang
- golang http/transport 程式碼分析GolangHTTP
- JS彈幕程式碼分析JS
- kvm-PLE程式碼分析
- 程式碼覆蓋率分析
- 1初學程式碼分析
- ABAP程式碼分析工具 - 事務碼SQF
- sidecar-inject程式碼分析IDE
- YYModel程式碼分析筆記筆記
- qmail-local程式碼分析(轉)AI
- 無程式碼相對於低程式碼的優勢分析
- 程式碼效能分析-Dottrace跟蹤程式碼執行時間
- 【程式碼視覺化實踐】程式碼變更影響分析視覺化
- Hacking Team攻擊程式碼分析
- Linux下init程式原始碼分析Linux原始碼
- Swift iOS : 程式碼分析DrawControllerSwiftiOSController
- mimalloc記憶體分配程式碼分析記憶體
- a002-pylint 程式碼分析
- 紅黑樹核心程式碼分析(JAVA)Java
- Java程式碼中字串拼接方式分析Java字串
- 用程式碼複雜度分析風險複雜度
- 程式碼測試覆蓋率分析
- 案例分析之JavaScript程式碼優化JavaScript優化
- 程式碼分析/統計工具彙總
- 武器系統邏輯程式碼分析
- Django任意程式碼執行漏洞分析Django
- 使用PL/Scope分析PL/SQL程式碼SQL
- IIS 錯誤程式碼分析手冊
- 二維陣列程式碼案例分析陣列
- Android UsbDeviceManager 程式碼分析Androiddev