首先理解什么是链式编程:链式编程,主要通过点‘.’来连接不同的函数调用 (这么定义,我也不清楚这么定义对不对,感觉这个定义怪怪得,将就这么理解算了)。但是oc语法特性,通过点操作连接不同的函数调用尤其还需要给调用函数传入参数并不是那么容易的一件事,首先来看一下:几种编程思想(链式编程、响应式编程、函数式编程) - phxiang的博客 - CSDN博客。
oc 语言链式(函数式)编程代表有Masonry,PromiseKit,他们的共性是 1、点连接无参数方法或属性。2、返回对象是相同的实例对象或是能返回实例对象的block方法。
上图是masonry的一个应用,方法top 、left 、and 等,都是返回MASConstraint实例对象且不需要传入参数的方法。
方法equalTo,是一个带返回值是MASConstraint实例对象的block,这样,当我们传入参数时就会触发block执行并返回MASConstraint实例对象,这样链式结构得以延续。
认识完在oc结构上是如何实现链式结构的,下面我们分析一下如何使用promiseKit实现链式结构的:
关于promiseKit内部的实现原理,请参考PromiseKit原理篇,弄清楚promisekit原理以后,我个人觉得,每次想让链式结构的值往下传递,还需要传入一个新的Anypromise对象,利用block块往下传递,感觉代码上并不是很“对称”的结构,PromiseKit 只是 Promise 设计模式的一种实现方式,并不能为我们的 app 带来任何”看得到“的好处,而仅仅是为了满足我们对“代码美学”的追求,那么为了解决这种美学的问题,尝试使用promise的结构改进一下代码。如下,封装了一个MyPromise类,使用first类方法创建一个MyPromise对象,需要执行的代码块放在一个first方法的参数block内,如下先展示使用范例 ,after()使用GCD_After模拟异步耗时操作;
在then实例方法中,衔接的所有then方法中的block都会被收集起来放在一个数组中对block强引用防止其被释放,因为在收集完所有then.block之前暂时不触发block执行。
当衔接完所有的then.block和first.block以及catchError的block,然后使用start方法触发first.block,当获取到值时通过储存的then.block数组依此触发相应的blocks,并把值相互传递
当first块的block获取到需要的值时且无异常数据,通过执行excuteThenBlock吧值传个下一个将要触发的block执行,且剩下的then.block是通过递归往下执行并依此传递值的,数组中的block执行完成之后会被立即移除释放:
如有意见或建议可留言交流,若小弟有错误或不足之处望指出更正,互相交流学习。PromiseKit的功能远不止解决异步嵌套问题,还有类似GCD中dispatch_group的线程依赖等功能。此处的Mypromise仅仅是展示函数链式编程思想的demo,有兴趣的可以作为练手学习。
另外附上源码:
===============================================================
```
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class MyPromise;
typedefvoid(^CatchErrBlock)(NSError*err);
typedefvoid(^PassBlock)(idobj);
typedefvoid(^Adapter)(iddata,PassBlockpass);
typedefvoid(^FirsBlock)(PassBlockpass);
typedefMyPromise*(^__FirsBlock)(FirsBlockfirst);
@interfaceMyPromise :NSObject
/** 首先使用firstblock创建 MyPromise对象*/
+(MyPromise*)first:(FirsBlock)block;
@property (nonatomic,class ,copy ,readonly) __FirsBlock first;
/** then方法延续链接 */
@property (nonatomic ,copy ,readonly) MyPromise*(^then)(Adapter adapter);
/** 错误接收 */
@property(nonatomic,copy,readonly)MyPromise*(^catchError)(CatchErrBlockerrBlock);
/** 开始执行启动 */
-(void)start;
@end
NS_ASSUME_NONNULL_END
===============================================================
#import "MyPromise.h"
@interface MyPromise ()
@property (nonatomic , strong) FirsBlock firstBlock;
@property (nonatomic , strong) CatchErrBlock errorBlock;
@property (nonatomic , strong) NSMutableArray *adapterBlocks;
@end
@implementation MyPromise
-(instancetype)init {
if(self== [superinit]) {
self.adapterBlocks= [[NSMutableArrayalloc]init];
}
return self;
}
+(MyPromise*)first:(FirsBlock)block {
MyPromise*promise = [[MyPromisealloc]init];
promise.firstBlock= block;
returnpromise;
}
+(__FirsBlock)first {
return^(FirsBlockfirst){
MyPromise*promise = [[MyPromisealloc]init];
promise.firstBlock= first;
returnpromise;
};
}
-(MyPromise*_Nonnull(^)(Adapter_Nonnull))then {
__weak typeof(self) weakSelf = self;
return^(Adapteradapter){
if(adapter) [weakSelf.adapterBlocksaddObject:adapter];
returnweakSelf;
};
}
-(MyPromise*_Nonnull(^)(CatchErrBlock_Nonnull))catchError {
__weak typeof(self) weakSelf = self;
return^(CatchErrBlockerrBlock){
if(errBlock) weakSelf.errorBlock= errBlock;
returnweakSelf;
};
}
-(void)start {
FirsBlock block = self.firstBlock;
PassBlockpass = ^(iddata){
/** 为保持MyPromise生命周期在异步环境下延长,此处使用强引用 */
if([dataisKindOfClass:[NSErrorclass]]) {
[selfexcuteErrorBlock:data];
}else{
[selfexcuteThenBlock:data];
}
};
block(pass);
}
-(void)excuteThenBlock:(id)data {
Adapter adapter = self.adapterBlocks.firstObject;
PassBlockpass = ^(idnewData){
/** 为保持MyPromise生命周期在异步环境下延长,此处使用强引用 */
if([newDataisKindOfClass:[NSErrorclass]]) {
[selfexcuteErrorBlock:newData];
}else{
[selfexcuteThenBlock:newData];
}
};
if(adapter) {
[self.adapterBlocksremoveObject:adapter];
adapter(data ,pass);
}
}
-(void)excuteErrorBlock:(NSError*)error {
CatchErrBlock errBlock = self.errorBlock;
if(errBlock) {
errBlock(error);
}
}
@end
```