链式编程
在使用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中的实现:(一)链式编程