博客> OC-链式编程
OC-链式编程
2019-08-24 07:01 评论:0 阅读:1053 小飞向前冲
ios 语法 链式编程

girly_desk-wallpaper-2400x1350.jpg 最近看到了链式编程和函数式编程这两个概念,网上查了一些资料,我心中存在的这几个疑惑根本没有帮我解开。 1,为什么可以使用.语法来调用方法,而不是使用OC里面的[ ]。 2,为什么方法里面明明没有参数,当方法的返回值是一个block时,使用.语法调用方法可以传参数。 不知道有没有和我遇到一样的问题的同学,周末的时间用了一个下午一点一点琢磨。下面我来一一解释这些问题。 首先,先简绍下block

block的作用,主要用于回调传值,解耦合 1.当方法的参数是block类型,block的参数用于从内向外传值 2.block的返回值用于从外向内返回结果

这两句话是能实现链式编程的关键,好好理解下。

对于block,我们可以这样理解 举个例子:大家想必都看过警匪片,在片子里面,坏蛋都喜欢安装一些遥控的炸弹来威胁警察,如果警察不听话,坏蛋就会引爆炸弹。这里的炸弹就可以理解为block,而遥控器就可以理解为调用block。block就是我们提前存储的一段代码块,当我们需要使用的时候才去调用它。下面来看一段网络请求数据的代码来理解下这个遥控炸弹。

@interface ReuuestData : NSObject

//定义无参数无返回值的block
- (void)requestDataSucc:(void(^)())succBlock fail:(void(^)())failBlock;
@end
@implementation ReuuestData
- (void)requestDataSucc:(void (^)())succBlock fail:(void (^)())failBlock {
//这一块就相当于炸弹的遥控器, 在成功或者失败里引爆炸弹
    if (/* DISABLES CODE */ (YES)) {
        succBlock(); //成功的block
    }else {
        failBlock(); //失败的block
    }
}
@end

下面我们在ViewController里使用下

ReuuestData *obj = [ReuuestData alloc] init];
//这一块就相当于埋了一个炸弹, 我们不管他什么时候会爆炸,决定他爆炸的时刻是安装了这个炸弹接收信号的遥控器
[obj requestDataSucc:^{
        //成功的回调
    } fail:^{
        //失败的回调
    }];

因为我们在进行网络请求时不知道什么时候请求会成功, 我们首先让obj 调用请求网络的方法, 此时,开始网络请求, 当请求结束后,会走对应的成功或者失败的方法。


接下来理解了上面的网络请求的例子,咱们就以一个例子来讲解链式编程,这个例子在其他文章中也都有介绍,为了保证文章的完整性,我就再啰嗦一遍这个例子。 1、首先创建一个Person类,并为其增加两个方法

  - (void)name1;
  - (void)sex1;

2、实现方法

 - (void) name1 {
   NSLog(@"name");
  }
  - (void) sex1 {
   NSLog(@"sex")
  }

3、实例化对象,并调用方法

 Person*person = [[Personalloc]init];
 [person name1];
 [person sex1];

以上三部很简单,相比大家都能看的懂。而我们最终的目标是

person.name(@"Jason").sex(@"男");

首先我们要实现

 [[person name] sex];

实现这个目标很简单,只需要调用[person name]的时候返回一个Person对象就可以了,下面我们来修改代码

- (Person *)name2;
  - (Person *)sex2;
- (Person *)name2 {
    NSLog(@"name");
    return self;
}
- (Person *)sex2 {
    NSLog(@"sex");
    return self;
}

这样就可以实现了,

[[person name2] sex2];

这离我们的最终目标还有一段距离。 先拆分下目标,先实现

    [[person name](@"Jason") sex](@"男"); //这个是不是和我们最终的目标很像,只需把中括号换成(.)就可以了

    person.name(@"Jason").sex(@"男");

我们都知道OC里面调用方法使用的是[obj func],向一个方法发送一条消息。而没有obj.func(param)的形式,要想实现这个,我们自然而然的会想到OC里面的block,只有block才是以()的方式调用的。 1、我们可以返回一个block,然后使用()去掉用。 2、那么我们要如何实现连续调用呢?我们再分析,要调用一个类的实例方法,必须由这个类的实例对象才能调用,如[[person name] sex]; 我们之所以可以连续使用[ ], 是因为我们返回了一个该实例对象。如果在返回的block里面返回一个实例对象不就可以连续调用了吗,而block正好也能返回一个对象。下面我们再改写代码。

//返回一个block,而block的返回值是一个Person对象
- (Person * (^)())name3;
- (Person * (^)())sex3;
- (Person * (^)())name3 {
//可以直接返回一个block
    return ^Person *(){
        return self;
    };
}
- (Person * (^)())sex3 {
//也可以定义一个block,接收返回的block,再把block返回
//等号右边的Person *()可以省略不写,Person *省略不写的原因是返回值本身可以不写,()可以省略不写的原因是block无参数时可以省略()
    Person *(^block)() = ^Person *(){
        return self;
    };
    return block;
}

此时我们调用方法

[[person name3]() sex3]();

看到如此的写法是不是很奇怪, OC里面调用方法没有这样写的呀,不要着急,我们一步一步分解。

[person name3]

这一步大家都很熟悉吧,我们在前面讲过,name3方法返回的是一个block,我们打印下这个返回值,看看到底是个什么鬼?

NSLog(@"%@", [person name3]);
//<__NSMallocBlock__: 0x7fa7714193c0>

打印结果告诉我们,返回值是一个block

//这种写法就相当于我们去调用了一个block,前面说过,只有block才会有()的这种语
//还记得前面讲的定时炸弹吗,当执行完[person name3]这句代码的时候,我们就找到了那个炸弹,再执行()的时候,我们就引爆了炸弹。
[person name3]()

前面说过,block也是有返回值的,我们同样打印下block的返回值

NSLog(@"%@", [person name3]());
<Person> //返回值是一个对象,很关键

接下来我们换一种写法来理解这句代码,把这句代码分成两步。

//1.声明一个返回值是Person类型, 无参数的block,用于接收返回值
    Person * (^returnBlock)() = [person name3];
    NSLog(@"%@", [person name3]); //<__NSMallocBlock__: 0x7f93bfc105a0>
    NSLog(@"%@", returnBlock);    //<__NSMallocBlock__: 0x7f93bfcb7a00>
//2.调用block
    returnBlock(); //注意此处返回的是一个Person对象, 这是我们实现链式编程的关键
    NSLog(@"%@", returnBlock()); //<Person>

上面的写法是不是容易理解一点呢,首先声明一个返回值是Person类型, 无参数的block,用于接收[person name3]的返回值, 此时我们找到了我们先前已经存好的block(炸弹),然后再去调用block(引爆炸弹) 下面我们再来看之前的代码

    [[person name3]() sex3]();
  //这一步操作分解之后就是这四步, 此处也能看出,链式编程能提高开发效率和提高代码的可读性
    Person * (^returnNameBlock)() = [person name3];
    Person *personName = returnNameBlock();
    Person * (^returnSexBlock)() = [personName sex3];
    Person *personSex = returnSexBlock();

此时离我们最终实现的效果的前一步还差一点,就是没有参数,我们再改写代码,加上参数,实现下面的效果

[[person name](@"Jason") sex](@"男");

改写

// 函数的返回值是一个Block,Block的返回值是当前对象,Block有一个参数
- (Person * (^)(NSString *name))name4; //设置参数
- (Person * (^)(NSString *sex))sex4;
- (Person * (^)(NSString *name))name4 {
// 方法的返回是一个”有参有返回值的Block“
    return ^Person *(NSString *name){
        NSLog(@"%@", name);
// block的返回值是当前对象
        return self;
    };
}
- (Person * (^)(NSString *sex))sex4 {
    return ^Person *(NSString *sex) {
        NSLog(@"%@", sex);
        return self;
    };
}

//调用

//(@"Jason")这个参数是block的参数,并且是block作为返回值的参数,在文章的开头已经说过,block的返回值参数用于从外向内返回结果,这就是为什么我们在外面传入参数(@"Jason"),而在函数内部会打印我们传入的参数,这点不要搞错。--&gt;>解释文章开头第二个问题
[[person name4](@"Jason") sex4](@"男");

OK,经历九九八十一难,我们终于到达了天竺,离求取真经只差一步了 我们要的结果是

 person.name(@"Jason").sex(@"男");

而我们现在的结果和这个目标只差一步了,是不是感觉有点小激动呢。 还是文章开头的第一个问题-->> 为什么可以使用.语法来调用方法,而不是使用OC里面的[ ]。 首先我们要知道OC里面的属性声明默认是私有的,外部是不可以直接访问的,而setter个getter方法是可以间接访问属性的。其实在使用self.propert语法时,不是直接访问属性,而是隐士的调用了setter或者getter方法来访问属性的,在编译与运行期间,并不关心你是否在真正的在获取一个属性。要实现使用.点语法调用方法,你的方法,必须有一个返回值,并且无参数。这样才符合getter方法的书写规范,而我们最终使用的方法整好符合这种规范。所以,你就可以直接使用 person.name(@"Jason").sex(@"男");来调用方法了。

最终我们实现了

 person.name(@"Jason").sex(@"男");

你可以尽情的 . 下去了


关于文章的第一个问题 -->> 为什么可以使用.语法来调用方法,而不是使用OC里面的[ ], 不知道自己的理解是否准确,如果有错误,还请帮忙纠正。

收藏
0
sina weixin mail 回到顶部