hi,你好!欢迎访问本站!登录
本站由网站地图腾讯云宝塔系统阿里云强势驱动
当前位置:首页 - 教程 - 杂谈 - 正文 君子好学,自强不息!

iOS 组件化计划

2019-11-18杂谈搜奇网41°c
A+ A-

一、基础归纳综合

解说

在组件化之前,app都是在一个工程里开辟的,开辟的职员也是比较少的,营业生长也不是异常快,项目中不援用组件化开辟也是适宜的。然则当开辟职员愈来愈多,代码量也就愈来愈多,营业也就愈来愈庞杂,这时刻单一的开辟情势会显露出一些弊病:

  • 轻易涌现争执(运用xib)
  • 耦合较严峻(代码没有明白的束缚,代码痴肥)
  • 开辟效力不够高

为了处置惩罚这些题目,因而涌现了组件化开辟的战略,能带来优点以下:

  • 加速编译速率
  • 自由选择开辟姿态(MVC/MVVM/FRP)
  • 轻易有针对性测试
  • 进步开辟效力

本日我们解说一下组件化开辟,也是自身项目中运用到的一种体式格局Target-Action体式格局。主假如基于Mediator情势和Target-Action,中心采用了Runtime来完成挪用。这套组件化将长途和当地运用挪用做了拆分,而且是当地挪用是为长途挪用供应效劳。

下面是target-action的工作图:

 挪用体式格局:

当地组件挪用:当地组件A在一处挪用[[CTMediator sharedInstance] performTarget:targetName action:actionName params:@{...}]向CTMediator发起了跨组件挪用,CTMediator依据发送过来的target和action,然后经由OC的runtime机制转为target实例以及action,末了挪用到目的营业供应的逻辑,完成要求.

长途运用的调取:长途运用是经由历程openURL的体式格局,由iOS 体系依据info.plist里的scheme设置用来能够找到相应的URL的运用,运用直接经由历程AppDelegate吸收到URL以后,挪用了CTMediator的OpenURL要领将吸收到的信息传入进去.固然,CTMediator也能够用CTMediator的openURL:options:体式格局趁便将option也吸收,这取决因而否包含了option数据,传入URL以后,CTMediator举行剖析URL,将要求的路由到相对应的target-action中,随后的历程就变成了上面的当地运用挪用历程了,终究完成了相应.

对应着以下:

 

 组件化计划中的去Model化设想

组件化挪用的时刻,应当设想是要对参数做去Model化的。假如组件间挪用不对的参数做去Model化的设想,就会致使有题目-营业情势上被组件化了,而现实上是没有自力。

假如模块A和模块B采用了Model化的计划,挪用要领时通报的参数就是一个对象,因而运用对象化的参数不管是面向接口,带来的效果就是营业模块情势上被组件化了,然则实质上照样没有被自力。

在跨模块场景中,参数最好照样运用去Model化的的体式格局去通报,在苹果开辟中,也就是以字典的情势去通报。如许就能够做到只需挪用方依靠mediator,而相应方是不须要依靠mediator。

在去Model的组件化的计划中,影响效力的总有两个:

  • 挪用方怎样晓得吸收方须要哪些参数呢?
  • 挪用方怎样晓得有哪些target能够被挪用呢?

实在背面题目,不管是不是是具有去Model都有这个题目,然则为何要一同说了呢,由于下面供应一种计划来将涌现的两个题目一同处置惩罚。

 

处置惩罚计划运用category

该计划基因而mediator和target-action情势组件化开辟,经由历程运行时完成挪用。mediator维护着若干个category,一个category对应着一个target,而一个target能够包含着多个action。

我们也能够如许明白:一个营业组件包含了一个category组件,这个category中有个mediator的category,而category中有一个target,这个target对应着此营业组件。target中又有若干个接口要领,用来其他营业来猎取该营业组件中的营业。

category自身是一种组合情势,依据差别的分类供应差别要领,此时每一个组件都是一个分类。

if (indexPath.row == 0) {
        UIViewController *viewController = [[CTMediator sharedInstance] CTMediator_viewControllerForDetail];

        // 取得view controller以后,在这类场景下,究竟push照样present,现实上是要由运用者决议的,mediator只需给出view controller的实例就好了
        [self presentViewController:viewController animated:YES completion:nil];
    }

    if (indexPath.row == 1) {
        UIViewController *viewController = [[CTMediator sharedInstance] CTMediator_viewControllerForDetail];
        [self.navigationController pushViewController:viewController animated:YES];
    }

    if (indexPath.row == 2) {
        // 这类场景下,很明显是须要被present的,所以没必要返回实例,mediator直接present了
        [[CTMediator sharedInstance] CTMediator_presentImage:[UIImage imageNamed:@"image"]];
    }

    if (indexPath.row == 3) {
        // 这类场景下,参数有题目,因而须要在流程中做优点理
        [[CTMediator sharedInstance] CTMediator_presentImage:nil];
    }

    if (indexPath.row == 4) {
        [[CTMediator sharedInstance] CTMediator_showAlertWithMessage:@"casa" cancelAction:nil confirmAction:^(NSDictionary *info) {
            // 做你想做的事
            NSLog(@"%@", info);
        }];
    }

上面就是我对Mediator下target-action情势的基础内容解说,下面解说CTMediator代码的详细完成体式格局!!!

 

二、代码解说

2.1 猎取CTMediator单例对象

#pragma mark - public methods
+ (instancetype)sharedInstance
{
    static CTMediator *mediator;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mediator = [[CTMediator alloc] init];
    });
    return mediator;
}

 

2.2 长途App挪用进口

/*
 scheme://[target]/[action]?[params]
 
 url sample:
 aaa://targetA/actionB?id=1234&title=title
 
 [url query]:  id=1234&title=title
 [url path]:  /actionB
 [url host]:  targetA
 */

- (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion
{
    //url参数的处置惩罚
    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;
}

 

2.3 当地组件挪用的进口

/**
 *  当地组件挪用进口
 *
 *  @param targetName 类对象   OC中类对象是要Target_为前缀的
 *  @param actionName 要领称号  末了现实挪用的是以Action_为前缀的
 *  @param params     参数
 *  @param shouldCacheTarget 是不是缓存拼接后的类对象
 *
 *  @return return value JSon花样的字符串
 */
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{
    //供swift项目运用
    NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName];
    
    // generate target
    NSString *targetClassString = nil;
    if (swiftModuleName.length > 0) {
        targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];
    } else {
        // 拼装类字符串
        targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
    }
    //先从缓存中取对象
    NSObject *target = self.cachedTarget[targetClassString];
    if (target == nil) {
        //不存在直接依据字符串建立类,而且初始化对象
        Class targetClass = NSClassFromString(targetClassString);
        target = [[targetClass alloc] init];
    }

    // 拼装要领字符串
    NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
    // 生成SEL
    SEL action = NSSelectorFromString(actionString);
    //先从缓存取,取不到去建立,然则也有能够建立失利的状况(targetName值不正确)
    if (target == nil) {
        // 这里是处置惩罚无相应要求的处所之一,这个demo做得比较简朴,假如没有能够相应的target,就直接return了。现实开辟历程当中是能够事先给一个牢固的target特地用于在这个时刻顶上,然后处置惩罚这类要求的
        [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
        return nil;
    }
    // 是不是缓存该对象
    if (shouldCacheTarget) {
        self.cachedTarget[targetClassString] = target;
    }
    // 该对象是不是能相应调起该要领
    if ([target respondsToSelector:action]) {
        return [self safePerformAction:action target:target params:params];
    } else {
        // 这里是处置惩罚无相应要求的处所,假如无相应,则尝试挪用对应target的notFound要领一致处置惩罚
        SEL action = NSSelectorFromString(@"notFound:");
        if ([target respondsToSelector:action]) {
            return [self safePerformAction:action target:target params:params];
        } else {
            // 这里也是处置惩罚无相应要求的处所,在notFound都没有的时刻,这个demo是直接return了。现实开辟历程当中,能够用前面提到的牢固的target顶上的。
            [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
            [self.cachedTarget removeObjectForKey:targetClassString];
            return nil;
        }
    }
}

注重: 想要挪用此要领,定义的类必需假如Target_为前缀的,而且要领必需是Action为前缀的!!!

别的代码也对无相应的状况分了两种状况:

  1. target == nil会触发NoTargetActionResponseWithTargetString这个要领
  2. action不能相应的时刻,会先挪用notFound要领,假如notFound还没有相应,依旧照样会挪用NoTargetActionResponseWithTargetString要领

 在现实的开辟中,能够给无相应的事宜提早做一个牢固的target,顶上这类特殊状况.

#pragma mark - private methods
- (void)NoTargetActionResponseWithTargetString:(NSString *)targetString selectorString:(NSString *)selectorString originParams:(NSDictionary *)originParams
{
    SEL action = NSSelectorFromString(@"Action_response:");
    NSObject *target = [[NSClassFromString(@"Target_NoTargetAction") alloc] init];
    
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    params[@"originParams"] = originParams;
    params[@"targetString"] = targetString;
    params[@"selectorString"] = selectorString;
    
    [self safePerformAction:action target:target params:params];
}

注重:代码中Target_NoTargetAction用来一致处置惩罚无相应的时刻给的牢固的target,action_response就是用来挪用的要领.

 

 2.4 消灭缓存

#pragma mark - private methods
- (void)NoTargetActionResponseWithTargetString:(NSString *)targetString selectorString:(NSString *)selectorString originParams:(NSDictionary *)originParams
{
    SEL action = NSSelectorFromString(@"Action_response:");
    NSObject *target = [[NSClassFromString(@"Target_NoTargetAction") alloc] init];
    
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    params[@"originParams"] = originParams;
    params[@"targetString"] = targetString;
    params[@"selectorString"] = selectorString;
    
    [self safePerformAction:action target:target params:params];
}

上面是CTMediator的详细代码,人人能够多看看关于当地组件挪用的完成代码!!! 下面解说本项目中运用到的详细内容.

 

三、项目运用

从智能引擎搜刮效果页-交易商详情页界面如上,逾越了两个模块,项目采用了CTMediator的Target-Action情势.下面根据实行的递次截图以下:

3.1 点击tableViewCell->func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)

项目采用的体式格局是MVP架构情势!!!

 

3.2 进入openBrokerDetailScene,最先调CTMediator

 

3.3 最先进入EngineToBroker_viewController,挪用self.performTarget("Broker", action: "brokerDetailVC", params: params, shouldCacheTarget: false)

 

3.4 最先进入Target_类,以及Action_要领中

 

经由这层层进入,终究得到了viewController

 

3.5 终究得到了ViewController,回到行将跳转到

 

上面就是全部项目中运用的体式格局,愿望对人人有所协助!!! 

 

  选择打赏方式
微信赞助

打赏

QQ钱包

打赏

支付宝赞助

打赏

  移步手机端
iOS 组件化计划

1、打开你手机的二维码扫描APP
2、扫描左则的二维码
3、点击扫描获得的网址
4、可以在手机端阅读此文章
未定义标签

本文来源:搜奇网

本文地址:https://www.sou7.cn/281982.html

关注我们:微信搜索“搜奇网”添加我为好友

版权声明: 本文仅代表作者个人观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。请记住本站网址https://www.sou7.cn/搜奇网。

发表评论

选填

必填

必填

选填

请拖动滑块解锁
>>