一切都从一个需求开始
- 项目需求: 设计一个计算器, 可以方便地进行加减乘除等计算
常规做法
- 设计一个计算类CalculateMaker, 通过其属性result记录计算结果, 当我需要计算10-2+6+2-1时, 代码如下:
CalculateMaker *make = [[CalculateMaker alloc] init];
make.result += 10;
make.result -= 2;
make.result += 6;
make.result += 2;
make.result -= 1;
NSLog(@"result = %zd", make.result);
非常规做法
- 看到以上代码时, 很多人可能会很烦中间的计算, 光是5次计算就占用了5行代码, 有没有办法可以一行解决. 这就引出了今天的主题, 即链式编程. 我们直接看使用链式编程是如何进行计算的:
NSUInteger result = [NSObject makeCalculator:^(CalculateMaker *make) {
make.add(10).minus(2).add(6).add(1).minus(5);
}];
NSLog(@"result = %zd", result);
- 先不管以上代码的实现原理, 单单是从代码结构上来讲, 理论上我们可以仅仅使用一行代码来实现无穷无尽的加减计算(尽管这行代码可能会很长, 但它也是一行). 关键是使用这种写法, 视觉上也更符合计算等式的逻辑.
- 那么问题来了, 这段代码是如何做到等价于10-2+6+2-1? 如何做到连续使用点语法? 又是如何做到用(10), (2)这种格式来传值, 并最终得出我们想要的结果呢? 我们把疑问归结为以下三个来逐一分析.
疑问大全
1. CalculateMaker类是什么?
2. NSObject的类方法makeCalculator方法做了什么?
3. make.add(10).minus(2)是如何做到等价于10-2的, 并且连续使用点语法的?
[疑问一] CalculateMaker类是什么?
@interface CalculateMaker : NSObject
@property (nonatomic, assign) NSUInteger result;
- (CalculateMaker * (^)(int value))add;
- (CalculateMaker * (^)(int value))minus;
@end
- 以上是CalculateMaker类的.h文件, 而根据代码我们不难得出结论, CalculateMaker只负责两件事:
- 一是提供result属性记录计算结果
- 二是提供add方法, minus方法进行加法计算和减法计算. (add和minus方法的实现稍后解释)
[疑问二] NSObject的类方法makeCalculator方法做了什么?
- makeCalculator其实是一个NSObject的类拓展方法, 该方法提供一个block参数, 同时返回计算结果, 详细代码如下:
- (NSUInteger)makeCalculator:(void(^)(CalculateMaker *make))block{
CalculateMaker *make = [[CalculateMaker alloc] init]; //1. 初始化一个CalculateMaker类
block(make); //2. 把CalculateMaker通过block传出去, 也等价于block所涉及的计算直接在这里执行
return make.result; //3. CalculateMaker执行完相关计算后, 把结果通过函数返回值传递出去
}
[疑问三] make.add(10).minus(2)是如何做到等价于10-2的, 并且连续使用点语法的?
(在这里我们仅以add为例进行解释)
- (CalculateMaker *(^)(int))add{
return ^CalculateMaker * (int value) {
self.result += value;
return self;
};
}
- 以上是add方法的实现, 其本质即执行一段block.
- 当代码执行make.add(10), CalculateMaker调用add方法执行这段block, 此时10作为 ^CalculateMaker * (int value)这个block的value参数传入, 传入后用于进行加法运算, 然后这段block又返回CalculateMaker自己.
- 总结来讲, 通过执行make.add(10), CalculateMaker既实现了+10的运算并记录到result中, 而且还把自己作为返回值重新传递出来, 此时我们就可以拿到CalculateMaker, 并继续使用点语法调用minus继续进行计算.
后话
- 眼尖的读者可能会发现, 这不就是Masonry框架的写法吗?!!! 没错, Masonry正是使用了链式编程来实现对NSLayoutConstraint的封装, 同样使用链式编程的还有远近驰名的Reative Cocoa. 对于常年写业务逻辑代码的工程师而言, 链式编程可能极少会用到, 但是了解链式编程其实不仅可以拓宽编程的认知水平, 而且也能进一步加深对block的理解, 最后如果想进一步了解Masonry实现细节以及链式编程的, 可以参阅以下两篇文章做更深层的学习.
[1]https://www.jianshu.com/p/2d94a0e579d2
[2]https://www.cnblogs.com/ludashi/archive/2016/07/11/5591572.html