[Objective-C]用Block实现链式编程

看这篇博客时最快让你上手ReactiveCocoa之基础篇看到作者介绍链式编程那一块,发现自己的钻研精神不足。想想自己使用链式编程也有段时间了,对,就是 Masonry 库。自己一直享受点语法带来的效率提升,却没想过自己去照着实现一下,真是惭愧。

好吧,本着发现问题就要立即解决问题的一贯原则,就看一看链式语法的实现方法。

现在做一个加减乘除计算。

在 Masonry 里,我们常用的添加约束的方法就是 mas_makeConstraints:

// View+MASAdditions
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    block(constraintMaker);
    return [constraintMaker install];
}

在约束里,用到 left, right 等是 constraintMaker 的属性,最后设置参数,比如 offset() 是 MASConstraint 的方法:

//MASConstraintMaker
@property (nonatomic, strong, readonly) MASConstraint *left;

- (MASConstraint *)left {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}

//MASConstraint
- (MASConstraint * (^)(CGFloat offset))offset;

- (MASConstraint * (^)(CGFloat))offset {
    return ^id(CGFloat offset){
        self.offset = offset;
        return self;
    };
}

所以源码大概的结构是这样。然后就要实现我们的计算器了。

//NSObject+Extension
@implementation NSObject (Calculator)

- (CGFloat)makeCalculators:(void (^)(CalculatorMaker *))block {
    CalculatorMaker *mgr = [[CalculatorMaker alloc] init];
    
    block(mgr);
    
    return mgr.result;
}

@end

//CalculatorMaker.h
@interface CalculatorMaker : NSObject

@property (nonatomic, assign) int result;

- (CalculatorMaker *(^)(int)) add;
- (CalculatorMaker *(^)(int)) sub;
- (CalculatorMaker *(^)(int)) mul;
- (CalculatorMaker *(^)(int)) div;

@end


//CalculatorMaker.m
@implementation CalculatorMaker

- (CalculatorMaker *(^)(int))add {
    return ^CalculatorMaker *(int value) {
        _result += value;
        return self;
    };
}

- (CalculatorMaker *(^)(int))sub {
    return ^CalculatorMaker *(int value) {
        _result -= value;
        return self;
    };
}

- (CalculatorMaker *(^)(int))mul {
    return ^CalculatorMaker *(int value) {
        _result *= value;
        return self;
    };
}

- (CalculatorMaker *(^)(int))div {
    return ^CalculatorMaker *(int value) {
        _result /= value;
        return self;
    };
}

@end

//main.m
int result = [NSObject makeCalculators:^(CalculatorMaker *make) {
          make.add(1).add(5).mul(2);
        }];
//output 12

看 add() 方法,返回类型是自身所在 CalculatorMaker 类型的 Block,而 Block 返回的也是 self,所以可以连续使用链式调用。不过关于为什么可以用点语法,这个我有点晕,按说 make.add 是 OC 语法,后面括号跑到 Block 里去。那对于 OC 语法来说,点语法是针对 getter 方法的,对于没有参数的非 getter 方法也可以调用,但调用时会报 warning ,所以这点不是很懂。

关于链式调用的写法,有点秀技术的感觉。视情况而用。不过,鉴于 Masonry 作者能写出这么有技巧的代码,我觉得有必要研究一下 Masonry 源码了。