• iOS组件化方案


    一、蘑菇街url-block方案

      这是蘑菇街中应用的一种页面间调用的方式,通过在启动时注册组件提供的服务,把调用组件使用的url和组件提供的服务block对应起来,保存到内存中。在使用组件的服务时,通过url找到对应的block,然后获取服务。

      具体实现代码如下:

    //Mediator.m 中间件
    @implementation Mediator
    typedef void (^componentBlock) (id param);
    @property (nonatomic, storng) NSMutableDictionary *cache
    - (void)registerURLPattern:(NSString *)urlPattern toHandler:(componentBlock)blk {
      [cache setObject:blk forKey:urlPattern];
    }
    - (void)openURL:(NSString *)url withParam:(id)param {
      componentBlock blk = [cache objectForKey:url];
      if (blk) blk(param); } @end

    1.先在Mediator中注册url-block

    #import "Mediator.h"
    #import "WRBookDetailViewController.h"
    + (void)initComponent {
      [[Mediator sharedInstance] registerURLPattern:@"weread://bookDetail" toHandler:^(NSDictionary *param) {
        WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:param[@"bookId"]];
        [[UIApplication sharedApplication].keyWindow.rootViewController.navigationController pushViewController:detailVC animated:YES];
      }]; }

    2.通过url在中间件中就能找到对应的block,并执行跳转

    #import "Mediator.h"
    + (void)gotoDetail:(NSString *)bookId {
      [[Mediator sharedInstance] openURL:@"weread://bookDetail" withParam:@{@"bookId": bookId}]; }

     二、 蘑菇街protocol-class方案

      针对url-block方案无法传递非正规参数,新增一种protocol-class方案

    //ProtocolMediator.m 中间件
    @implementation ProtocolMediator
    @property (nonatomic, storng) NSMutableDictionary *protocolCache
    - (void)registerProtocol:(Protocol *)proto forClass:(Class)cls {
      NSMutableDictionary *protocolCache;
      [protocolCache setObject:cls forKey:NSStringFromProtocol(proto)];
    }
     
    - (Class)classForProtocol:(Protocol *)proto {
      return protocolCache[NSStringFromProtocol(proto)];
    }
    @end
    

     每一个组件都有一个公共Protocol文件,定义了每一个组件对外提供的接口

    //ComponentProtocol.h
    @protocol BookDetailComponentProtocol <NSObject>
    - (UIViewController *)bookDetailController:(NSString *)bookId;
    - (UIImage *)coverImageWithBookId:(NSString *)bookId;
    @end
     
    @protocol ReviewComponentProtocol <NSObject>
    - (UIViewController *)ReviewController:(NSString *)bookId;
    @end
    
    
    //BookDetailComponent 组件
    #import "ProtocolMediator.h"
    #import "ComponentProtocol.h"
    #import "WRBookDetailViewController.h"
    + (void)initComponent{
      [[ProtocolMediator sharedInstance] registerProtocol:@protocol(BookDetailComponentProtocol) forClass:[self class];
    }
     
    - (UIViewController *)bookDetailController:(NSString *)bookId {
      WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:param[@"bookId"]];
      return detailVC;
    }
     
    - (UIImage *)coverImageWithBookId:(NSString *)bookId {
      ….
    }
    

     最后调用者通过 protocol 从 ProtocolMediator 拿到提供这些方法的 Class,再进行调用:

    //WRReadingViewController.m 调用者
    //ReadingViewController.m
    #import "ProtocolMediator.h"
    #import "ComponentProtocol.h"
    + (void)gotoDetail:(NSString *)bookId {
      Class cls = [[ProtocolMediator sharedInstance] classForProtocol:BookDetailComponentProtocol];
      id bookDetailComponent = [[cls alloc] init];
      UIViewController *vc = [bookDetailComponent bookDetailController:bookId];
      [self.navigationController pushViewController:vc animated:YES];
    }
    

     三、target-action方案

      1.先设置一个中间件公共类

    @interface CTMediator : NSObject
    
    + (instancetype)sharedInstance;
    
    // 远程App调用入口
    - (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion;
    // 本地组件调用入口
    - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;
    - (void)releaseCachedTargetWithTargetName:(NSString *)targetName;
    
    @end
    
    //
    //  CTMediator.m
    //  CTMediator
    //
    //  Created by casa on 16/3/13.
    //  Copyright © 2016年 casa. All rights reserved.
    //
    
    #import "CTMediator.h"
    
    @interface CTMediator ()
    
    @property (nonatomic, strong) NSMutableDictionary *cachedTarget;
    
    @end
    
    @implementation CTMediator
    
    #pragma mark - public methods
    + (instancetype)sharedInstance
    {
        static CTMediator *mediator;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            mediator = [[CTMediator alloc] init];
        });
        return mediator;
    }
    
    /*
     scheme://[target]/[action]?[params]
     
     url sample:
     aaa://targetA/actionB?id=1234
     */
    
    - (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion
    {
        NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
        NSString *urlString = [url query];
        for (NSString *param in [urlString componentsSeparatedByString:@"&"]) {
            NSArray *elts = [param componentsSeparatedByString:@"="];
            if([elts count] < 2) continue;
            [params setObject:[elts lastObject] forKey:[elts firstObject]];
        }
        
        // 这里这么写主要是出于安全考虑,防止黑客通过远程方式调用本地模块。这里的做法足以应对绝大多数场景,如果要求更加严苛,也可以做更加复杂的安全逻辑。
        NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
        if ([actionName hasPrefix:@"native"]) {
            return @(NO);
        }
        
        // 这个demo针对URL的路由处理非常简单,就只是取对应的target名字和method名字,但这已经足以应对绝大部份需求。如果需要拓展,可以在这个方法调用之前加入完整的路由逻辑
        id result = [self performTarget:url.host action:actionName params:params shouldCacheTarget:NO];
        if (completion) {
            if (result) {
                completion(@{@"result":result});
            } else {
                completion(nil);
            }
        }
        return result;
    }
    
    - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
    {
        
        NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
        NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
        
        id target = self.cachedTarget[targetClassString];
        if (target == nil) {
            Class targetClass = NSClassFromString(targetClassString);
            target = [[targetClass alloc] init];
        }
        
        SEL action = NSSelectorFromString(actionString);
        
        if (target == nil) {
            // 这里是处理无响应请求的地方之一,这个demo做得比较简单,如果没有可以响应的target,就直接return了。实际开发过程中是可以事先给一个固定的target专门用于在这个时候顶上,然后处理这种请求的
            return nil;
        }
        
        if (shouldCacheTarget) {
            self.cachedTarget[targetClassString] = target;
        }
        
        if ([target respondsToSelector:action]) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            return [target performSelector:action withObject:params];
    #pragma clang diagnostic pop
        } else {
            // 这里是处理无响应请求的地方,如果无响应,则尝试调用对应target的notFound方法统一处理
            SEL action = NSSelectorFromString(@"notFound:");
            if ([target respondsToSelector:action]) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                return [target performSelector:action withObject:params];
    #pragma clang diagnostic pop
            } else {
                // 这里也是处理无响应请求的地方,在notFound都没有的时候,这个demo是直接return了。实际开发过程中,可以用前面提到的固定的target顶上的。
                [self.cachedTarget removeObjectForKey:targetClassString];
                return nil;
            }
        }
    }
    
    - (void)releaseCachedTargetWithTargetName:(NSString *)targetName
    {
        NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
        [self.cachedTarget removeObjectForKey:targetClassString];
    }
    
    #pragma mark - getters and setters
    - (NSMutableDictionary *)cachedTarget
    {
        if (_cachedTarget == nil) {
            _cachedTarget = [[NSMutableDictionary alloc] init];
        }
        return _cachedTarget;
    }
    
    @end
    

        2.每个组件创建一个Category,在其中实现当前组件的跳转实现

    #import <CTMediator/CTMediator.h>
    #import <UIKit/UIKit.h>
    
    @interface CTMediator (TAConfirmOrder)
    
    - (UIViewController *)confirmOrderViewControllerWithGoodsId:(NSString *)goodsId goodsName:(NSString *)goodsName ConfirmComplete:(dispatch_block_t)confirmComplete;
    
    @end
    
    #import "CTMediator+TAConfirmOrder.h"
    
    @implementation CTMediator (TAConfirmOrder)
    
    - (UIViewController *)confirmOrderViewControllerWithGoodsId:(NSString *)goodsId goodsName:(NSString *)goodsName ConfirmComplete:(dispatch_block_t)confirmComplete
    {
        NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
        params[@"goodsId"] = goodsId;
        params[@"goodsName"] = goodsName;
        params[@"completeBlock"] = confirmComplete;
        return [self performTarget:@"TAConfirmOrder" action:@"ConfirmOrderViewController" params:params shouldCacheTarget:NO];
    }
    
    @end
    

     3.通过中间件调用

      UIViewController *confirmOrderVC = [[CTMediator sharedInstance] confirmOrderViewControllerWithGoodsId:self.goodsId  goodsName:self.goodsName  ConfirmComplete:^{

            //...

       }];

  • 相关阅读:
    SVN服务器搭建和使用(三)
    SVN服务器搭建和使用(二)
    SVN服务器搭建和使用(一)
    mysql和oracle分页
    Windows许可证 即将过期
    Java 设计模式学习笔记1——策略模式(Duck例子)
    java 抽象类与接口
    java servlet练习测试
    windows 添加开始菜单
    visio连接线设置
  • 原文地址:https://www.cnblogs.com/liuluoxing/p/9542864.html
Copyright © 2020-2023  润新知