MGJRouter代碼分析

今天來聊聊組件化,之前一直聽說大廠在搞,什么淘寶架構(gòu),什么蘑菇街,既然談到了架構(gòu)的問題,那必屬重中之重。接下來分析一下蘑菇街開源的代碼,自己做個(gè)總結(jié)。

引入

類書本的文章個(gè)人感覺還是寫不來的,再搬到自己寫的東西這來也不合適,所以直接上一鏈接,通過鏈接文章大致可了解下它的前身后世,產(chǎn)生原因,以及整體宏觀架構(gòu)設(shè)計(jì),而我接下來要做的是細(xì)化,以及轉(zhuǎn)化,便于自己吸收 ---------> 組件化架構(gòu)漫談

1. 話不多說,先看入口:
image_1c25qj3m5ejq111g1v5lvkgjek9.png-7.7kB
image_1c25qj3m5ejq111g1v5lvkgjek9.png-7.7kB
@interface MGJRouter ()
/**
 *  保存了所有已注冊的 URL
 *  結(jié)構(gòu)類似 @{@"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;
}

很明顯,蘑菇街架構(gòu)(以下簡稱MGJ)通過該單例作管理,統(tǒng)一進(jìn)行調(diào)配,而該單例僅有一個(gè)變量,就是routes,實(shí)際上它僅僅是管理了一個(gè)字典的結(jié)構(gòu),具體字典內(nèi)有哪些內(nèi)容,我們慢慢看;

2. 回調(diào)Block的定義
/**
 *  routerParameters 里內(nèi)置的幾個(gè)參數(shù)會(huì)用到上面定義的 string
 */
typedef void (^MGJRouterHandler)(NSDictionary *routerParameters);

/**
 *  需要返回一個(gè) object,配合 objectForURL: 使用
 */
typedef id (^MGJRouterObjectHandler)(NSDictionary *routerParameters);

上面這兩個(gè)block定義是MGJ注冊URL的回調(diào),一個(gè)帶返回值,另一個(gè)不帶,在這里我們說一下帶返回值的block用法;如下舉例 ------>

WX20180107-134538@2x.png-37.5kB
WX20180107-134538@2x.png-37.5kB

//聲明
typedef UIViewController *(^ViewControllerHandler)();

//作參數(shù)
@interface DemoListViewController : UIViewController
+ (void)registerWithTitle:(NSString *)title handler:(ViewControllerHandler)handler;
@end

//定義
@implementation DemoListViewController
+ (void)registerWithTitle:(NSString *)title handler:(ViewControllerHandler)handler
{
    UIViewController* vc = handler()
}
@end

//在別處調(diào)用
@implementation DemoDetailViewController
[DemoListViewController registerWithTitle:@"基本使用" handler:^UIViewController *{
        return DemoDetailViewController();
}];
@end

如上,我們把ViewControllerHandler的運(yùn)行延遲到了實(shí)際調(diào)用的時(shí)刻,并且我們可以在這個(gè)handler的實(shí)現(xiàn)中帶入很多信息;

3. MGJ數(shù)據(jù)結(jié)構(gòu)管理
extern NSString *const MGJRouterParameterURL;
extern NSString *const MGJRouterParameterCompletion;
extern NSString *const MGJRouterParameterUserInfo;
//*************************************************
static NSString * const MGJ_ROUTER_WILDCARD_CHARACTER = @"~";  //這是一個(gè)占位符
static NSString *specialCharacters = @"/?&.";

NSString *const MGJRouterParameterURL = @"MGJRouterParameterURL";
NSString *const MGJRouterParameterCompletion = @"MGJRouterParameterCompletion";
NSString *const MGJRouterParameterUserInfo = @"MGJRouterParameterUserInfo";

從這里我們可以看出,MGJ的路由管理,實(shí)際上是一個(gè)解析url以及對(duì)應(yīng)的管理,我們舉幾個(gè)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我們可以看出,路由的管理實(shí)際上就是url的解析過程,下面我們來具體看一下解析過程;

4. URL解析
  • route url
WX20180107-150437@2x.png-59.1kB
WX20180107-150437@2x.png-59.1kB
- (NSArray*)pathComponentsFromURL:(NSString*)URL
{
    NSMutableArray *pathComponents = [NSMutableArray array];
    if ([URL rangeOfString:@"://"].location != NSNotFound) {
        NSArray *pathSegments = [URL componentsSeparatedByString:@"://"];
        // 如果 URL 包含協(xié)議,那么把協(xié)議作為第一個(gè)元素放進(jìn)去
        [pathComponents addObject:pathSegments[0]];
        
        // 如果只有協(xié)議,那么放一個(gè)占位符
        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


    WX20180107-151956@2x.png-111.8kB
    WX20180107-151956@2x.png-111.8kB
- (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解析
WX20180107-160319@2x.png-147.8kB
WX20180107-160319@2x.png-147.8kB
- (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) {
        
        // 對(duì) key 進(jìn)行排序,這樣可以把 ~ 放到最后
        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 對(duì)應(yīng)的 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;
        }
        //所以注冊路由時(shí)的回調(diào)是在這里才調(diào)用到的
        //也就是openURL響應(yīng)了register的回調(diào)
        if (handler) {
            [parameters removeObjectForKey:@"block"];
            handler(parameters);
        }
    }
}
5. 后續(xù)?
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容