今天讲点iOS开发中如何妙用Block的链式编程来让代码更加清爽。
一.在iOS开发中,不带参数的方法是可以通过点语法来调用的。
示例代码:
/** 声明person类 */
@interface Person : NSObject
- (void)study;
@end
/** 实现person类 */
@implementation Person
- (void)study
{
NSLog(@"学习");
}
@end
/** 调用person类 */
int main(int argc,const char * argv[]) {
@autoreleasepool {
Person *p = [[Personalloc] init];
p.study; // 这里编译器会报警告,因为你的study并没有返回值,编译器不建议如此调用方法
}
return0;
}
二.程序中的地址
我们知道一个指针代表这一个地址,通过这个地址,我们可以对地址里的信息进行访问
其实函数,block,所有非指针数据类型也都是有地址的。而它们的函数名,变量名在底层处理中会变为函数与变量的地址,从而实现了对函数的调用,对变量的访问。
我定义一个函数
void test ()
{
printf("测试");
}
其实“test”就代表着这段代码的地址
我定义一个block
void (^myBlock)() = ^(){
NSLog(@"我的block");
};
我们用runtime去rewrite这个block时会发现,在底层中,block会被变为一段函数,并且block中拥有isa指针,这以为着block其实也是一种对象或者结构体(有点跑题了...)
总而言之,我们可以很愚昧的把函数名,block名看做那段源码的地址,从而达到对那段源码的访问。
说了这么多,你可能会想这跟我们今天的主题有什么关系呢?哈哈,原谅我卖这么多关子,这些都是印子,有助于我们理解Block的链式编程。
好了,废话不多说,进入我们今天的主题--如何用block实现链式编程!
三.用Block实现链式编程
首先我先把实现代码写出来
如下:
/** 类的声明 */
@interface Person : NSObject
- (Person *(^)())eat;
- (Person *(^)())sleep;
@end
/** 类的实现 */
@implementation Person
- (Person *(^)())eat
{
return ^(){
NSLog(@"吃饭");
returnself;
};
}
- (Person *(^)())sleep
{
return ^(){
NSLog(@"睡觉");
returnself;
};
}
@end
/** 调用 */
int main(int argc,const char * argv[]) {
@autoreleasepool {
Person *p = [[Personalloc] init];
p.eat().sleep().eat().sleep(); // 在这里我们就可以优雅的使用Block的链式编程,怎么样有木有感觉很清爽?
}
return0;
}
看了上面的代码,你可能觉得蒙蒙的,没关系我们来一步一步分析它是如何实现的
首先我们看eat方法与sleep方法的声明,我们声明了这两个方法,因为无参,所以我们可以通过点语法直接调用。这两个方法都要求返回一个Person *(^)()类型的block
其次我们看eat方法与sleep方法的实现,我们老老实实的返回了一个Person *(^)()类型的block,OK语法是没有问题的!
最后我们看main函数里的调用 p.eat() 首先我们通过点语法调用了eat方法,这个eat方法返回了一个Person *(^)()类型的block的内存地址,紧接着就是(),我们拿着返回的block给它带了个(),这就调用了我们返回的block,这个block执行完毕后又返回了Person类型的指针,于是我们又可以开开心心的去调用Person的实例方法!这样我们就达到了链式编程的目的。
四.升级版,如何带参!
这里我们讨论,如何能给刚刚的eat和sleep方法带上参数!毕竟没有参数的方法过分局限。
同样,我还是先贴上代码:
/** 声明 */
@interface Person : NSObject
- (Person *(^)(NSString *))eat;
- (Person *(^)(NSString *))sleep;
@end
/** 实现 */
@implementation Person
- (Person *(^)(NSString *))eat
{
return ^(NSString *foodName){
NSLog(@"吃%@", foodName);
returnself;
};
}
- (Person *(^)(NSString *))sleep
{
return ^(NSString *who){
NSLog(@"睡%@", who);
returnself;
};
}
@end
/** 调用 */
int main(int argc,const char * argv[]) {
@autoreleasepool {
Person *p = [[Personalloc] init];
p.eat(@"大便味的咖喱").sleep(@"最漂亮的女人"); // 有木有一种oc,java傻傻分不清楚的感觉~~
}
return0;
}
首先你可能会想,给方法带参不就是eat:(NSString *)foodName嘛,但是这样是不行的,因为oc的语法限制,带上了参数我们便不能通过点语法去调用方法了。
所以我们必须换一种思路,这种思路就是:通过block的参数来给方法传参。
先看声明部分,eat与sleep都要求我们返回一个Person *(^)(NSString *)类型的block,我们再看这个block的类型,一种返回Person指针且传NSString *的block。
我们再看声明部分,我们在方法内的的确确的返回了一个Person *(^)(NSString *)类型的block,OK,语法还是正确的。
最后我们看调用,首先p.eat返回了一个Person *(^)(NSString *)类型的block的block,这个block后面就是一个(),并且这个()内部有一个参数,这不正是调用这个block的正确格式嘛!!!block执行完毕后,又返回了Person 类型的指针,于是乎我们又可以开开心心的调用Person的实例方法了。就这样我们借助了block的参数,达到了给方法传参的目的!
五.结尾总结
Block的链式编程其实就是借助了c语言的语法特性,通过函数的返回值与手写(),从而达到了调用block的目的。
希望大家可以借鉴这个知识点来让自己的code变得更加清爽。