iOS 包看包会的链式编程

作者: Codepgq | 来源:发表于2017-07-08 14:50 被阅读546次

    <br />
    ......此处省略10000字,作为一个程序员,代码才是文字,是的,开撸。

    <br />

    1 一步一步来:创建一个对象,并且创建两个方法 eat 和 run

    声明方法 实现方法

    现在我们可以这样子调用方法

     Result * res = [[Result alloc] init];
     [res eat];
     [res run];
    

    <br />

    2 如何实现把eatrun写在一行上

    [[res eat2] run2];
    

    分析一下,对象方法是由对象调用,当我们调用eat2之后在调用run2方法,run2是对象方法,只有对象调用,那是不是我在调用eat2方法之后返回调用对象就可以了?

    声明方法 实现方法

    <br />

    • 效果图


      效果图

    <br />

    • 果不其然,这样子就可以做到了,但是有现在有一个问题啊,调用eat方法,到底吃啥我不知道,我也决定不了。我想决定自己吃啥,不能老是让你决定是吧。

    <br />
    于是我们就可以这样子:吃啥,跑多远外界决定

    声明方法 实现方法

    现在我们就要完成这样子的效果了

    [[[res eat3:@"apple"] eat3:@"banana"] run3:100.0];
    
    • 效果图


      效果

    <br />

    下面我们看看block的写法

    我们想类似于Masonry那样子去调用怎么写呢?
    先来完成一个eat4run4

    #pragma mark - 点语法
    - (void(^)())eat4;
    - (void(^)())run4;
    
    

    实现

    - (void(^)())eat4{
        void(^eat4Block)() = ^(void){
            NSLog(@"eat4");
        };
        return eat4Block;
    }
    - (void(^)())run4{
        
        return ^(void){
            NSLog(@"run4");
        };
    }
    
    • 分析
    ok,看我们这里做了什么,首先我们定义了一个有返回值的方法,返回值是一个没有返回值,没有参数的block,
    然后我们在实现文件里面用两种方式去返回了这个block。
    然后们就可以在外界这样子调用了
    res.eat4();
    res.run4();
    

    为什么可以这样调用呢?

    • 分析
    block的写法
    void (^a)() = ^{
       NSLog(@"调用了block");
    };
    a();
    从上面的代码可以看得出来,调用a()的时候就会执行block里面的代码,在上面我们定义的方法返回一个block
    我们可以这样子写:
    void (^x)() = [res eat4];
    x();
    效果是一样的,
    .eat4调动的就是get方法,然后我们拿到了一个block,
    这个时候我么你直接(),就相当于上面的两步,所以我们可以这样子调用
    

    <br />
    然后我们发现,还是不可以链式啊,还是要写两行的,根据上面所述的对象方法的理念,我们可以这样子。

    #pragma mark - 点语法 链式 不带参数
    - (Result*(^)())eat5;
    - (Result*(^)())run5;
    

    实现文件

    - (Result*(^)())eat5{
        
        Result *(^eat5Block)() = ^{
            NSLog(@"eat5");
            return self;
        };
        
        return eat5Block;
    }
    - (Result*(^)())run5{
        return  ^{
            NSLog(@"run 5");
            return self;
        };
    }
    

    上面的代码也是两种方式去实现,但是看最后的代码就可以发现,把self 返回出去了,即是当前的对象返回出去了,所以我们就可以这样子写了

    res.eat5().eat4();
    

    <br />

    • ok,经过上面的代码之后,这个时候还是只能调用吃和跑,不能自己想吃什么就吃什么,想跑多远就跑多远,于是参照对象方法的写法,我们就可以这样子.
    #pragma mark - 点语法 链式 带参数
    - (Result*(^)(NSString * food))eat6;
    - (Result*(^)(float metter))run6;
    

    实现

    - (Result*(^)(NSString * food))eat6{
        Result *(^eat6Block)(NSString * food) = ^(NSString * food){
            NSLog(@"eat6 %@",food);
            return self;
        };
        return eat6Block;
    }
    - (Result*(^)(float metter))run6{
        return ^(float metter){
            NSLog(@"run6 %.2f",metter);
            return self;
        };
    }
    

    <br />

    • ok 到这里,我们就不在 吃 和 跑 了

    <br >

    没错,我们换成了 jumpplay ,来接着看看链式

    首先还是一样实现最基本的用法
    在项目中,下面的方法我们经常会用

    #pragma mark - 普通对象方法 - 外界处理 不带参数
    - (void)jump:(void(^)())block;
    - (void)play:(void(^)())block;
    
    #pragma mark - 普通对象方法 - 外界处理 带参数
    - (void)jump2:(void(^)(float metter))block;
    - (void)play2:(void(^)(NSString *string))block;
    

    实现

    - (void)jump:(void(^)())block{
        if (block) {
            block();
        }
    }
    
    - (void)play:(void(^)())block{
        if (block) {
            block();
        }
    }
    
    - (void)jump2:(void(^)(float metter))block{
        if (block) {
            block(100);
        }
    }
    - (void)play2:(void(^)(NSString *string))block{
        if (block) {
            block(@"basketball");
        }
    }
    

    调用如下:

     [res jump:^{
         NSLog(@"我来控制输出什么");
     }];
     [res play:^{
         NSLog(@"我来决定玩什么");
     }];
     [res jump2:^(float metter) {
         NSLog(@"被控制要跳%.2f米",metter);
     }];
     [res play2:^(NSString *string) {
         NSLog(@"被要求玩%@",string);
     }];
    

    <br />

    上面两种调用方式是不是很常见啊,然后我们结合两种方式一起,再结合链式来玩玩吧。

    先来试试没有参数

    定义

    实现

    实现
    • 效果图
    类是这样子

    <br />

    带参数

    <br />

    定义

    实现

    实现

    调用

    调用

    <br />

    上面看了那么久的普通调用方式,现在我们来玩玩block的形式的

    首先定义方法

    #pragma mark - 点语法 - 传入参数,内部自己处理,返回参数,自己决定做什么
    -(void(^)(float metter, void(^)(float metter)))jum5;
    -(void(^)(NSString* str, void(^)(NSString* str)))play5;
    

    实现方法

    -(void(^)(float metter, void(^)(float metter)))jum5{
        void (^jum5Block)(float metter, void(^)(float metter)) = ^(float metter, void(^neibuBlock)(float metter)){
            if (neibuBlock) {
                neibuBlock(metter);
            }
        };
        return jum5Block;
    }
    -(void(^)(NSString* str, void(^)(NSString* str)))play5{
        return ^(NSString* str, void(^neibuBlock)(NSString* str)){
            if (neibuBlock) {
                neibuBlock(str);
            }
        };
    }
    

    但是我们调用得这个样子调用

    res.jum5(123,^(float metter){
        NSLog(@"我自己决定我跳%.2f米",metter);
    });
    res.play5(@"我给你100块",^(NSString * str){
        NSLog(@"%@,你给我一个篮球",str);
    });
    

    效果图

    <br />
    上面的调用方式明显不爽嘛,换成链式的,其实就是返回一个有返回值且返回值是调用的block
    定义

    #pragma mark - 点语法 - 传入参数,内部自己处理,返回参数,自己决定做什么 链式
    -(Result*(^)(float metter, void(^)(float metter)))jum6;
    -(Result*(^)(NSString* str, void(^)(NSString* str)))play6;
    

    实现

    -(Result*(^)(float metter, void(^)(float metter)))jum6{
        Result* (^jum6Block)(float metter, void(^)(float metter)) = ^(float metter, void(^neibuBlock)(float metter)){
            if (neibuBlock) {
                neibuBlock(metter * 1.2);
            }
            
            return self;
        };
        return jum6Block;
    }
    -(Result*(^)(NSString* str, void(^)(NSString* str)))play6{
        return ^(NSString * str, void(^neibuBlock)(NSString * str)){
            
            if (neibuBlock) {
                neibuBlock([NSString stringWithFormat:@"加一个漂亮的签名 %@",str]);
            }
            
            return self;
        };
    }
    

    效果图

    效果图

    到这里为止,了解阶段就完成了,下面要开始实现了一个有用的东西了。

    首先我们如果实现一个弹窗,带确定、取消按钮,同时可以输入账户、密码,还不包括处理文字输入情况,我们就需要写这么多代码

    UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"牛逼" message:@"💯就是这么强" preferredStyle:UIAlertControllerStyleActionSheet];
        UIAlertAction * action1 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            NSLog(@"点击了取消");
        }];
        [alert addAction:action1];
        UIAlertAction * action2 = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            NSLog(@"点击了确定");
        }];
        [alert addAction:action2];
        
        [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
            
        }];
        
        [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
            
        }];
        
        [self presentViewController:alert animated:YES completion:nil];
        
    

    <br />

    但是如果我们用链式编程,可以做到实时监听输入情况,外界控制是否密文显示,并且扩张性很强。

    PQAlertController *alert= [PQAlertController alertControllerWithTitle:@"难道我这么帅" message:@"帅,不存在的!" preferredStyle:UIAlertControllerStyleAlert];
    alert.addAction(@"取消",^{
            NSLog(@"cancel");
        }).addAction(@"确定",^{
            NSLog(@"ok");
        }).addTextInput(@"请输入账户",NO,^(NSString *text,UITextField *textField){
            NSLog(@"%@",text);
        }).addTextInput(@"请输入密码",YES,^(NSString *text,UITextField *textField){
            NSLog(@"%@",text);
        });
    [self presentViewController:alert animated:YES completion:nil];
    
    实现类似RAC的text_signal

    感谢ZAQ大神带入门


    简单的介绍一下PQAlertcontroller吧
    1 同时支持iphone ,ipad
    2 支持链式调用
    3 可以实时监听textField输入回调

    alertController.gif

    Demo 附上

    用star、喜欢砸死我

    相关文章

      网友评论

      • 伦敦乡下的小作家:大佬,学习完了你的demo,想了解下,实际开发中,在什么情景下用链式的比较多
        Codepgq:@伦敦乡下的小作家 我也是半吊子...
        伦敦乡下的小作家:@Codepgq 老哥稳哦,近期打算好好读一读你的RAC文章,虽然之前用过,但不是很扎实
        Codepgq:@伦敦乡下的小作家 比如那个alert controller的时候,看心情吧:joy::joy::joy:
      • 有趣程序猿:思路很清晰,值得借鉴
        Codepgq:@China最小的奋斗者 谢谢
      • iCode_:如果你打印运行时间,block的链式会多耗时差不多一半的时间,所以在需要频繁调用的地方不建议用这种方式。
        Codepgq:我不知道你所指的是哪个代码,然后我就就测试了一下我demo中封装的alertController这个,分别用两种方式进行调用,得到两组测试数据(每个测试5次,测试开始时间是创建之前,测试结束时间是显示之后,计算两者的时间差 使用的是这个方法获取时间CFAbsoluteTimeGetCurrent(),使用机器6p,测试方式:编译到手机,每次打开点击显示)
        非block链式
        0.017218
        0.021022
        0.019553
        0.018715
        0.016700
        block链式
        0.018646
        0.016878
        0.018261
        0.017682
        0.014913
      • 化二缺:已经 star
        Codepgq:@飞镖有毒 多谢兄台
      • 雪_晟:可以的啊 感觉链式很简洁
        Codepgq:是的呢

      本文标题:iOS 包看包会的链式编程

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