转自
Objective-C 链式编程
最近看到了链式编程和函数式编程这两个概念,网上查了一些资料,但是发现资料都千篇一律,我心中存在的这几个疑惑根本没有帮我解开。
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: 0x7fd9e9daf980> //返回值是一个对象,很关键
接下来我们换一种写法来理解这句代码,把这句代码分成两步。
//1.声明一个返回值是Person类型, 无参数的block,用于接收返回值
Person * (^returnBlock)() = [person name3];
NSLog(@"%@", [person name3]); //<NSMallocBlock: 0x7f93bfc105a0>
NSLog(@"%@", returnBlock); //<NSMallocBlock: 0x7f93bfcb7a00>
//2.调用block
returnBlock(); //注意此处返回的是一个Person对象, 这是我们实现链式编程的关键
NSLog(@"%@", returnBlock()); //<Person: 0x7fd9e9daf980>
上面的写法是不是容易理解一点呢,首先声明一个返回值是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 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"),而在函数内部会打印我们传入的参数,这点不要搞错。-->>解释文章开头第二个问题
person name4 sex4;
OK,经历九九八十一难,我们终于到达了天竺,离求取真经只差一步了
我们要的结果是
person.name(@"Jason").sex(@"男");
而我们现在的结果和这个目标只差一步了,是不是感觉有点小激动呢。
还是文章开头的第一个问题-->> 为什么可以使用.语法来调用方法,而不是使用OC里面的[ ]。
首先我们要知道OC里面的属性声明默认是私有的,外部是不可以直接访问的,而setter个getter方法是可以间接访问属性的。其实在使用self.propert语法时,不是直接访问属性,而是隐士的调用了setter或者getter方法来访问属性的,在编译与运行期间,并不关心你是否在真正的在获取一个属性。要实现使用.点语法调用方法,你的方法,必须有一个返回值,并且无参数。这样才符合getter方法的书写规范,而我们最终使用的方法整好符合这种规范。所以,你就可以直接使用 person.name(@"Jason").sex(@"男");来调用方法了。
最终我们实现了
person.name(@"Jason").sex(@"男");
你可以尽情的 . 下去了