黑魔法:iOS链式编程

链式编程

在使用SDAutoLayout 自动视图布局时就已经发现这种链式编程。通过"." 将属性链接在一起形成如同一条链的编程方法。

 self.totalMoneyCount.sd_layout
.leftEqualToView(self)
.rightEqualToView(self)
.topSpaceToView(self.totalMoney, AutoHeight(15))
.autoHeightRatio(0);

核心实现原理:只要返回是本身这个对象

下列是普通方法的实现,稍候会对两个方法进行改变,形成一个普通的链式。

//Person 类 两个类中均无返回
-(void)goShopping{
    //todo
}

-(void)runing{
    //todo
}

Person *person = [[Person alloc]init];
[person goShopping];
[person runing]; 

普通方法的链式编程

//Person 类 两个类中均返回了本身
-(Person *)goShopping{
    //todo
    return self;
}

-(Person *)runing{
    //todo
    return self;
}

Person *person = [[Person alloc]init];
[[[person goShopping]runing]goShopping];

基本链式,你已经很明白了,但这种方式不美观,如果带参数,你会发现更不美观了,并且如果方法体中参数过长,串起来很难看。

Block形式的链式编程

如果你不太懂Block,那么我建议你先查看iOS之Block

-(Person* (^)(void))goShopping{
Person* (^block)(void) = ^() {
//在调用goShopping()Block时,执行了打印并返回自身,block执行完成,下一个链时,如果想让返回的block再调用对象的方法,那么这个block就需要返回一个对象,即返回block。
        NSLog(@"goShopping");
        return self;
    };
    return block;
}

-(Person* (^)(void))runing{

   Person* (^block)(void) = ^() {
        NSLog(@"runing");
        return self;
    };
    return block;
}

Person *person = [[Person alloc]init];
person.goShopping().runing().goShopping().runing();

到这里,你能很直观看见,block形式的链式比起普通方法的链式形式更美观。
其实很多人都有疑惑,(Person* (^)(void)) runing(^)(void)Person * 这啥意思?其实你可能和我一样,拆开看了,Person* (^)(void) 是一个以Block定义的整体,我第一次看有点懵,后来才看明白。现在我用typedef定义一下。

#import 

@interface Person : NSObject

//注意,typedef block形式要在interface之上才能被自动联想,然后移到interface之下,不然会已找不到Person而报错
typedef Person *(^PersonBlock)(void); 

@property(nonatomic,copy)PersonBlock personBlock;

-(PersonBlock)goShopping;
-(PersonBlock)runing;

@end

#import "Person.h"
@implementation Person

-(PersonBlock)goShopping{
    return ^(void){          
         //todo
         return self;
    };
}

-(PersonBlock)runing{
    return ^(void){
            //todo
        return self;
    };
}
@end

block里处理业务,最后return self 是为了返回本身,继续支持链式。

链式进价

你已经掌握了链式的结构,上面的链式足够你使用了,但你在实际使用时,发现block没有提示带值。完全需要手动,能明显感觉有缺陷。这时候就需要新增几个Block做成属性来替代普通方法。

我将删除无关的代码,包括上面提到过的,实现一个加法运算。

#import 
@interface Person : NSObject
typedef Person* (^Add)(CGFloat value);
// 加法,关于readonly:在调用时,防止出现self.addValue = ^Person *(CGFloat value) {};
@property(nonatomic,copy,readonly)Add addValue;
//计数和
@property(nonatomic,assign)CGFloat sum;
//运算
-(void)runIt;
@end

#import "Person.h"
@interface Person()
@property(nonatomic,copy)Add addValue;
@end
@implementation Person
-(Add)addValue{
    if (!_addValue) {

        //避免循环引用
        __weak typeof(self) weakSelf = self;
        _addValue = ^(CGFloat value){
            weakSelf.sum += value;
            return weakSelf;
        };
    }
    return _addValue;
}
-(void)runIt{
    self.addValue(2).addValue(45).addValue(56).addValue(5);
    NSLog(@"-->%lf",self.sum);
}
@end

链式编程进行分类扩展

因为我用的是Person类进行的加法运算,所以看起来有出处,很奇怪

#import 
#import "Person.h"
@interface NSObject (calculator)
+(CGFloat)Calculate:(void(^)(Person* person)) cal;
@end
@implementation NSObject (calculator)
+(CGFloat)Calculate:(void(^)(Person* person)) cal{
    Person *person = [[Person alloc]init];
    cal(person);
    return person.sum;
}
@end

使用

 CGFloat value = [NSObject Calculate:^(Person *person) {
        person.addValue(12).addValue(56).addValue(1);
    }];

    NSLog(@"->>>>%lf",value);

什么情况下适合链式编程

可以看出,链式编程像是“组合”的一种,不在乎调用的先后顺序(这句话可能不太准确,如果使用链式进行sql语句拼接,还是需要顺序),对于需要额外增加的,只需要新增一个属性。
并且调用方法可以一直调用同一个方法,比如:person.goShopping().goShopping().goShopping无限制。

所以总结:适合动态,可接收不在乎调用先后顺序,次数等动态需求。

至此,你已经掌握了链式编程了。

下一篇:黑魔法:iOS函数式编程

函数式编程和链式编程,微妙的关系


参考文

1. 深入浅出-iOS函数式编程的实现 && 响应式编程概念
2. 几种编程思想在iOS中的实现:(一)链式编程

你可能感兴趣的:(黑魔法:iOS链式编程)