博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PromiseKit入门 之 Objective-C
阅读量:7022 次
发布时间:2019-06-28

本文共 8087 字,大约阅读时间需要 26 分钟。

一、PromiseKit介绍

PromiseKit,优雅的的管理多个异步操作,让你从此远离多层嵌套。

用一个例子作比较:

需求:先注册,后登录,提示登录成功。

// 常规方法:嵌套执行多个任务[self signUpWithUserName:uid pwd:pwd resultBlock:^(BOOL success, NSString *message) {    if (success) {        [self signInWithUserName:uid pwd:pwd resultBlock:^(BOOL success, NSString *message) {            if (success) {                NSLog(@"登录成功");            }        }];    }}];复制代码
// PromiseKit提供的方法,简洁清晰的展示多任务操作signUpPromise.then(^{    return signInPromise;}).then(^{    NSLog(@"登录成功");}).catch(^(NSError* error){});复制代码

由此可见,用PromiseKit实现的方法更美观,且更易维护。

二、PromiseKit的使用

还是上面那个需求,下面是具体实现步骤:

1.  修改现有的注册任务和登录任务,如下:// 注册伪代码(登录代码类似)- (AnyPromise*)promise_signUpWithUserName:(NSString*)uid pwd:(NSString*)pwd {   return [AnyPromise promiseWithAdapterBlock:^(PMKAdapter  _Nonnull adapter) {          // 网络请求 xxx          if(注册成功) {            adapter(@"注册成功", nil);          }else {            adapter(nil,[self errorWithMessage:"注册失败!"]); // error自己定义          }    }];}- (NSError*)errorWithMessage:(NSString*)msg {    return [[NSError alloc] initWithDomain:NSNetServicesErrorDomain code:-101 userInfo:@{NSLocalizedDescriptionKey:msg}];}2.  在一个地方管理所有异步操作[HTTPUtil promise_signUpWithUserName:uid pwd:pwd].then(^{// 注册成功走这里    NSLog(@"注册成功");    return [HTTPUtil promise_signInWithUserName:uid pwd:pwd];// 接下来执行登录操作}).then(^(NSDate* date){// 登录成功走这里    NSLog(@"登录成功, 时间:%@",date);}).catch(^(NSError* error){// 注册失败或者登录失败跳到这里,不会执行then方法    NSLog(@"%@",error.userInfo[NSLocalizedDescriptionKey);});复制代码

接下来逐句解读该段代码,并穿插知识点。

2.1 创建任务

AnyPromise可以看作是任务,下文以任务代替。

- (AnyPromise*)promise_signUpWithUserName:(NSString*)uid pwd:(NSString*)pwd {   return [AnyPromise promiseWithAdapterBlock:^(PMKAdapter  _Nonnull adapter) {    }];}复制代码

任务的所有初始化方法如下,可在源码查看它们说明,这里不一一解释:

// 常用+ (instancetype __nonnull)promiseWithAdapterBlock:(void (^ __nonnull)(PMKAdapter __nonnull adapter))block; + (instancetype __nonnull)promiseWithResolverBlock:(void (^ __nonnull)(__nonnull PMKResolver))resolveBlock; + (instancetype __nonnull)promiseWithValue:(__nullable id)value;+ (instancetype __nonnull)promiseWithIntegerAdapterBlock:(void (^ __nonnull)(PMKIntegerAdapter __nonnull adapter))block;+ (instancetype __nonnull)promiseWithBooleanAdapterBlock:(void (^ __nonnull)(PMKBooleanAdapter __nonnull adapter))block;- (instancetype __nonnull)initWithResolver:(PMKResolver __strong __nonnull * __nonnull)resolver ;复制代码

2.2 处理Block

任务的初始化方法中附有一个Block:PMKAdapter,其返回的参数表达了该任务的处理结果是成功还是失败,如:

if(登录成功) {    adapter([NSDate date], nil);// 处理成功}else {    adapter(nil, error); // 处理失败}复制代码

任务有2种状态:pendingresolved(本人习惯翻译为:等待处理和处理结束), 其中resolved包括fulfilled(处理成功)和rejected(处理失败)。

为什么上段代码中adapter([NSDate date], nil);代表处理成功,而adapter(nil, error);代表处理失败呢?

接下来了解PMKAdapter

PMKAdapter可以传递2个参数,第一个是id类型的任何对象,可以是nil,第二个是NSError对象,也可以是nil。传递不同参数,任务的状态会不同,如下图所示:

因此可以总结PMKAdapter的参数:

总结一:如果任意一个参数的类型是NSError任务 处理失败

总结二:2个参数都是nil或者第一个是id,第二个是nil,任务 处理成功

处理结果将影响第三步的操作。

下面是所有Block的定义:

typedef void (^PMKAdapter)(id __nullable, NSError * __nullable) ;typedef void (^PMKResolver)(id __nullable);typedef void (^PMKIntegerAdapter)(NSInteger, NSError * __nullable) ;typedef void (^PMKBooleanAdapter)(BOOL, NSError * __nullable) ;复制代码

2.3 在一个地方管理所有异步操作

signUpPromise.then(^{// 注册成功走这里    NSLog(@"注册成功");    return signInPromise;// 返回signInPromise:接下来执行登录操作}).then(^(NSDate* date){// 登录成功走这里    NSLog(@"登录成功, 时间:%@",date);}).catch(^(NSError* error){// 注册失败或者登录失败跳到这里    NSLog(@"%@",error.userInfo[NSLocalizedDescriptionKey);});复制代码

如果任务 处理成功,就会执行紧接其后的then方法;如果处理失败,则会跳过后面的所有then方法,执行catch方法。

怎样才能使用Block传递过来的参数呢?

第一:如果任务 处理成功,可以在then方法里面手动增加最多3个参数:

.then(^(NSDate* date, ..., ...){    NSLog(@"date: %@",date);})复制代码

第二:如果任务 处理失败,可以把参数传给NSError

adapter(nil,[self errorWithMessage:"登录失败!"]);复制代码

然后在catch方法提取:

.catch(^(NSError* error){    NSLog(@"%@",error.userInfo[NSLocalizedDescriptionKey);})复制代码

三、PromiseKit之AnyPromise

AnyPromise是PromiseKit的关键类,一个AnyPromise对象可以看作是一个任务。

1 属性

@property (nonatomic, readonly) __nullable id value; // 已经处理的Promise的值@property (nonatomic, readonly) BOOL pending; // 等待处理@property (nonatomic, readonly) BOOL fulfilled; // 处理成功@property (nonatomic, readonly) BOOL rejected; // 处理失败复制代码

2 连接词

2.1 then

then:当任务 处理成功,在主线程执行。

- (AnyPromise * __nonnull (^ __nonnull)(id __nonnull))then;复制代码

thenInBackground:当任务 处理成功,在默认线程执行。

- (AnyPromise * __nonnull(^ __nonnull)(id __nonnull))thenInBackground;复制代码

thenOn:当任务 处理成功,在指定线程执行。

- (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, id __nonnull))thenOn;复制代码

2.2 catch

catch:当任务 处理失败,在主线程运行。

- (AnyPromise * __nonnull(^ __nonnull)(id __nonnull))catch;复制代码

catchInBackground:当任务 处理失败,在global线程执行。

- (AnyPromise * __nonnull(^ __nonnull)(id __nonnull))catchInBackground;复制代码

catchOn:当任务 处理失败,在指定线程执行。

- (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, id __nonnull))catchOn;复制代码

2.3 ensure

ensure:当任务 处理结束,在主线程执行。

- (AnyPromise * __nonnull(^ __nonnull)(dispatch_block_t __nonnull))ensure;复制代码

ensureOn:当任务 处理结束,在指定线程执行。

- (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, dispatch_block_t __nonnull))ensureOn;复制代码

所有连接词在指定线程执行的用法类似:

.ensureOn(queue, ^{})复制代码

3 进阶用法

3.1 PMKJoin

AnyPromise *__nonnull PMKJoin(NSArray * __nonnull promises);复制代码

等待所有任务 处理结束,执行下一步。(所有任务 处理结束才会处理处理失败,如果有就跳转到then方法)

官方注释:

PMKJoin waits on all provided promises, then rejects if any of those promises rejects, otherwise it fulfills with values from the provided promises.

大意:PMKJoin将会等待所有任务promises参数提供)处理结束,之后如果有一个任务 处理失败PMKJoin 才会处理失败,否则处理成功

例子:吃完饭,喝完汤,才能吃水果。

PMKJoin(@[ricePromise, soupPromise]).then(^(NSArray* messages){// 此时参数是数组类型    return fruitPromise;}).catch(^(NSError* error){    NSArray* promises = error.userInfo[PMKJoinPromisesKey];// 错误信息中包含了所有任务     for (AnyPromise* promise in promises) {        if (promise.rejected) {            NSLog(@"%@ is rejected",promise);        }     }});复制代码

3.2 PMKWhen

extern AnyPromise * __nonnull PMKWhen(id __nonnull input);复制代码

等待所有任务 处理成功,执行下一步。(一旦有任务 处理失败,立即跳转到then方法,且停止执行其他任务)

官方注释:

PMKWhen rejects as soon as one of the provided promises rejects.

大意:只要其中一个任务input参数提供)处理失败PMKWhen立即处理失败

例子:做饭,拿碗筷,才能吃饭。

PMKWhen(@[cookPromise, setPromise]).then(^(NSArray* messages){// 此时参数是数组类型    return eatPromise;}).catch(^(NSError* error) {// "处理失败"的任务    NSInteger index = [error.userInfo[PMKFailingPromiseIndexKey] integerValue];    NSLog(@"index:%@ error: %@\n", @(index),error.userInfo[NSLocalizedDescriptionKey]);});复制代码

3.3 PMKRace

extern AnyPromise * __nonnull PMKRace(NSArray * __nonnull promises);复制代码

只要处理结束一个任务,立即执行then或者catch方法,其他任务继续执行。(只关心第一个处理结束任务,不关心其他任务

例子:在众多算法中找出解题速度最快的那个。

PMKRace(@[algPromise1, algPromise2, algPromise3]).then(^(id message){    NSLog(@"%@",message);}).catch(^(NSError* error){    NSLog(@"%@",error.userInfo[NSLocalizedDescriptionKey]);});复制代码

3.4 PMKAfter

extern AnyPromise * __nonnull PMKAfter(NSTimeInterval duration);复制代码

延迟一定时间执行任务,相当于dispatch_after

例子:注册成功后3秒再登录。

signUpPromise.then(^{    // 注册成功    return PMKAfter(3.f).then(^{// 延迟执行signInPromise        return signInPromise;    });}).then(^{    // 登录成功}).catch(^(NSError* error){    // handle error});复制代码

3.5 PMKHang

extern id __nullable PMKHang(AnyPromise * __nonnull promise);复制代码

阻塞线程,直到任务 处理结束。这个做法不安全,只在调试时用!!!

例子:阻塞当前线程,直到注册成功或者注册失败才会执行next step。

id value = PMKHang([self promise_signUpWithUserName:uid pwd:pwd]);NSLog(@"%@ is resolved",value);// next step// ...复制代码

4. 综合使用

设定任务执行的超时时间

// 限定注册任务的超时时间为2秒PMKRace(@[PMKWhen([HTTPUtil promise_signUpWithUserName:uid pwd:pwd]), PMKAfter(2.).then(^{    NSLog(@"计时结束~");})]).then(^(NSString* msg){    NSLog(@"%@",msg);});复制代码

四、PromiseKit的集成(手动)

刚着手把PromiseKit集成到项目的时候,按照网上的教程,无论是CocoaPods还是手动安装都失败,经过摸索,步骤如下:

版本:Xcode 10.1,PromiseKit 6.8.4

  1. 把PromiseKit项目放在你的项目文件夹,如图:

  1. 然后把PromiseKit.xcodeproj文件拖到你的项目

  1. 前往Target-Embbedded Binaries添加PromiseKit.framework,如图:

  1. 导入头文件,编译
#import 
复制代码

此时可能报错:

解决方法:把Build Active Architecture Only属性改为YES

这样,就能在模拟器上使用PromiseKit。但是依然不能在真机上运行,可能报错:

解决方法:修改PromiseKit的可用架构

报错原因:由于在OC中使用了Swift,编译成功后Xcode会生成PromiseKit-Swift.h文件。如果编译失败,则没有此文件,因此报错。

到此,成功集成PromiseKit。

参考:

转载于:https://juejin.im/post/5cdf8cfef265da1b8b2b2d8b

你可能感兴趣的文章
路由、交换原理
查看>>
数据库数字参考表的妙用
查看>>
检查多个IP是否ping通的两种脚本
查看>>
numpy.asarray与theano.shared比较
查看>>
jQuery 实现 Checkbox 的全选、全不选和反向选择的功能
查看>>
Java环境搭建--JDK、tomcat
查看>>
CopyOnWriteArrayList详解
查看>>
静态多态和动态多态
查看>>
接口测试工具-poster
查看>>
java基础第十三天_IO
查看>>
apache快速开通虚拟主机shell
查看>>
【面试题】C语言:模拟实现strncpy,尝试比较strncpy与strcpy区别。
查看>>
16.1反射
查看>>
curl 的用法
查看>>
mysql 的主从复制原理
查看>>
Laravel的容器Vagrant+Homestead+Composer+Yaml开发环境搭建正确步骤
查看>>
centos6.8基础调优详解
查看>>
maven工程 使用hessian遇到的问题
查看>>
【安全牛学习笔记】Penetration testing presentation
查看>>
Windows mysql备份
查看>>