美文网首页
iOS模块化-模块间通信

iOS模块化-模块间通信

作者: p_peng | 来源:发表于2019-05-20 15:02 被阅读0次

    前言

    前面写过一篇《iOS 组件化》,里面介绍了组件化和模块化的区别,模块化可以简单理解为业务模块的组件化

    模块间通信

    APP按业务划分成若干个模块后,不同模块之间的通信不能像原来那样直接调用了,一个模块并不知道另一个模块的存在,所以需要一个中介者来处理。

    MGJRouter

    通过url-block(protocal-block)方式管理组件,先注册,再使用。

    例如商品详情页面

    // 注册商品详情URL
    [MGJRouter registerURLPattern:@"mgj://detail?id=:id" toHandler:^(NSDictionary *routerParameters) {
        NSNumber *id = routerParameters[@"id"];
        // create view controller with id
        // push view controller
    }];
    
    // 打开商品详情
    [MGJRouter openURL:@"mgj://detail?id=404"]
    

    注册时根据pathComponents构建一个多层的NSMutableDictionary,最后的block使用类似于Swift中的匿名参数_作为key。

    - (void)addURLPattern:(NSString *)URLPattern andHandler:(MGJRouterHandler)handler
    {
        NSMutableDictionary *subRoutes = [self addURLPattern:URLPattern];
        if (handler && subRoutes) {
            subRoutes[@"_"] = [handler copy];
        }
    }
    
    - (void)addURLPattern:(NSString *)URLPattern andObjectHandler:(MGJRouterObjectHandler)handler
    {
        NSMutableDictionary *subRoutes = [self addURLPattern:URLPattern];
        if (handler && subRoutes) {
            subRoutes[@"_"] = [handler copy];
        }
    }
    
    - (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;
    }
    

    CTMediator

    使用Target-Action方式,不需要注册,只要按规定命名规范命名Class和Selector即可。一个模块完成后,需要创建一个Target_{Module_Name}的类,把公开的方法(Action)添加到这里。然后新建一个CTMediator的Category,Category根据上面Target中的Action方法提供对外接口。

    Target_A.h

    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    
    @interface Target_A : NSObject
    
    - (UIViewController *)Action_nativeFetchDetailViewController:(NSDictionary *)params;
    - (id)Action_nativePresentImage:(NSDictionary *)params;
    - (id)Action_showAlert:(NSDictionary *)params;
    
    // 容错
    - (id)Action_nativeNoImage:(NSDictionary *)params;
    
    @end
    

    Category CTMediator+CTMediatorModuleAActions.h

    
    #import "CTMediator.h"
    #import <UIKit/UIKit.h>
    
    @interface CTMediator (CTMediatorModuleAActions)
    
    - (UIViewController *)CTMediator_viewControllerForDetail;
    
    - (void)CTMediator_showAlertWithMessage:(NSString *)message cancelAction:(void(^)(NSDictionary *info))cancelAction confirmAction:(void(^)(NSDictionary *info))confirmAction;
    
    - (void)CTMediator_presentImage:(UIImage *)image;
    
    - (UITableViewCell *)CTMediator_tableViewCellWithIdentifier:(NSString *)identifier tableView:(UITableView *)tableView;
    
    - (void)CTMediator_configTableViewCell:(UITableViewCell *)cell withTitle:(NSString *)title atIndexPath:(NSIndexPath *)indexPath;
    
    - (void)CTMediator_cleanTableViewCellTarget;
    
    @end
    

    在.m文件中主要是调用核心类CTMediator- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget方法。

    在该方法中首先根据参数构建Target和Action

    NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
    NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];

    上面的target如果是Swift,需要加上Swift中的Module

    NSString *targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];

    然后使用runtime,得到Class和Selector。CTMediator使用了NSInvocation来执行方法,根据返回值的不同,做了不同的调用方式。

    - (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params
    {
        NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
        if(methodSig == nil) {
            return nil;
        }
        const char* retType = [methodSig methodReturnType];
    
        if (strcmp(retType, @encode(void)) == 0) {
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
            [invocation setArgument:&params atIndex:2];
            [invocation setSelector:action];
            [invocation setTarget:target];
            [invocation invoke];
            return nil;
        }
    
        if (strcmp(retType, @encode(NSInteger)) == 0) {
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
            [invocation setArgument:&params atIndex:2];
            [invocation setSelector:action];
            [invocation setTarget:target];
            [invocation invoke];
            NSInteger result = 0;
            [invocation getReturnValue:&result];
            return @(result);
        }
    
        if (strcmp(retType, @encode(BOOL)) == 0) {
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
            [invocation setArgument:&params atIndex:2];
            [invocation setSelector:action];
            [invocation setTarget:target];
            [invocation invoke];
            BOOL result = 0;
            [invocation getReturnValue:&result];
            return @(result);
        }
    
        if (strcmp(retType, @encode(CGFloat)) == 0) {
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
            [invocation setArgument:&params atIndex:2];
            [invocation setSelector:action];
            [invocation setTarget:target];
            [invocation invoke];
            CGFloat result = 0;
            [invocation getReturnValue:&result];
            return @(result);
        }
    
        if (strcmp(retType, @encode(NSUInteger)) == 0) {
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
            [invocation setArgument:&params atIndex:2];
            [invocation setSelector:action];
            [invocation setTarget:target];
            [invocation invoke];
            NSUInteger result = 0;
            [invocation getReturnValue:&result];
            return @(result);
        }
    
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        return [target performSelector:action withObject:params];
    #pragma clang diagnostic pop
    }
    

    异步回调使用params的方式,有点隐晦,个人感觉没有MGJ的completion的方式直接。

    总结

    MGJRouter使用起来很简单,只要管理好需要使用的router就可以了。CTMediator使用Target-Action的方式也很优秀,我也是看了源代码才理解了Target-Action的概念。两个框架代码都比较简单,很容易看懂。个人比较喜欢MGJRouter的方式,也在网上看了它的变种,支持URL重写等功能,可以支持服务端下发,实现线上版本跳转切换。

    相关文章

      网友评论

          本文标题:iOS模块化-模块间通信

          本文链接:https://www.haomeiwen.com/subject/pidnaqtx.html