最近在尝试解决下载管理中,全部下载的速度问题,于是在github中试图寻找答案,无意中发现了Promise这个第三方库。它的作用如下
Future
is a sort of a placeholder object that you can create for a result that does not yet exist. Generally, the result of the
Future
is computed concurrently and can be later collected. Composing concurrent tasks in this way tends to result in faster, asynchronous, non-blocking parallel code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
@implementation
MyViewController
- (
void
)viewDidLoad {
void
(^handleError)(
id
) = ^(
NSString
*msg){
UIAlertView *alert = [[UIAlertView alloc] init… delegate:
self
…];
[alert show];
};
[
NSURLConnection
sendAsynchronousRequest:rq completionHandler:^(
id
response,
id
data,
id
error) {
if
(error) {
handle([error localizedDescription]);
}
else
{
UIImage *image = [UIImage imageWithData:data];
if
(!image) {
handleError(@
"Bad server response"
);
}
else
{
self
.imageView.image = image;
}
}
}];
}
- (
void
)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(
NSInteger
)buttonIndex {
// hopefully we won’t ever have multiple UIAlertViews handled in this class
[
self
dismissViewControllerAnimated:
YES
];
}
@end
|
1
2
3
4
5
6
7
8
9
10
11
12
|
#import <PromiseKit.h>
- (
void
)viewDidLoad {
self
.imageView.image = image;
}).
catch
(^(
NSError
*error){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:…];
[alert promise].then(^{
[
self
dismissViewControllerAnimated:
YES
];
})
});
}
|
A `Promise` represents the future value of a task.
To obtain the value of a `Promise`, call `then`. When the asynchronous task that this `Promise` represents has resolved successfully, the block you pass to `then` will be executed with the resolved value. If the `Promise` has already been resolved succesfully at the time you `then` the `Promise`, the block will be executed immediately.
Effective use of Promises involves chaining `then`s, where the return value from one `then` is fed as the value of the next, et cetera.
promise 代表了一个任务的未来的值,
1
2
3
4
|
UIAlertView * alert=[[UIAlertView alloc] initWithTitle:@
"title"
message:@
"message"
delegate:
nil
cancelButtonTitle:@
"sure"
otherButtonTitles:
nil
];
[alert promise].then(^(){
NSLog
(@
"excute!"
);
});
|
在创建 UI|AlertView之后调用它的promise对象,然后调用then就可以了。调用方法相当简单,具体使用可以参考:http://promisekit.org。
1
2
3
4
5
6
7
8
9
|
- (PMKPromise *)promise {
PMKAlertViewDelegater *d = [PMKAlertViewDelegater
new
];
PMKRetain(d);
self
.delegate = d;
[
self
show];
return
[PMKPromise
new
:^(
id
fulfiller,
id
rejecter){
d->fulfiller = fulfiller;
}];
}
|
它的promise方法很简单,仅仅就是创建并返回了一个PMKPromise对象,继续来看看promise的then方法,这个是最主要的方法,如果这个方法理解清楚了,这个框架就可以举一反三的大致理解清楚了。
1
2
3
4
5
|
- (PMKPromise *(^)(
id
))then {
return
^(
id
block){
return
self
.thenOn(dispatch_get_main_queue(), block);
};
}
|
我们看到then 他会调用thenOn方法,都返回的是一个block块。
- (PMKPromise *(^)(id))then 函数和调用形式[alert promise].then(^(){ NSLog(@"excute!");}) ,我们自己看看是不是感觉很奇怪,为什么then后面竟然带的是中括号,而我们通常在使用block的时候,一般都是带{}进行操作的,想想 [UIView animate:^{ }] ,是不是完全不一样,这里就涉及了一个block的用法了,就我来说看的和用的还是比较少的。区别如下:
1.通常,我们例如[UIView animate:^{ }] 都是在文件内部进行block的调用,而在客户端(调用的时候)来定义block块,我们通常用block块替换delegate的时候就是这么做得,也是大众的做法
2.此处,刚刚是反过来的,在内部定义block块,在外部调用,这样做得好处是在调用的时候就直接能执行。(想想promise是异步操作的一个库哦)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
- (PMKResolveOnQueueBlock)thenOn {
return
[
self
resolved:^(
id
result) {
if
(IsPromise(result))
return
((PMKPromise *)result).thenOn;
if
(IsError(result))
return
^(dispatch_queue_t q,
id
block) {
return
[PMKPromise promiseWithValue:result];
};
return
^(dispatch_queue_t q,
id
block) {
// HACK we seem to expose some bug in ARC where this block can
// be an NSStackBlock which then gets deallocated by the time
// we get around to using it. So we force it to be malloc'd.
block = [block
copy
];
return
dispatch_promise_on(q, ^{
return
safely_call_block(block, result);
});
};
}
pending:^(
id
result, PMKPromise *next, dispatch_queue_t q,
id
block, PMKPromiseFulfiller resolve) {
if
(IsError(result))
return
PMKResolve(next, result);
dispatch_async(q, ^{
resolve(safely_call_block(block, result));
});
}];
}
|
thenOn方法又调用了- (id)resolved:(PMKResolveOnQueueBlock(^)(id result))mkresolvedCallback pending:(void(^)(id result, PMKPromise *next, dispatch_queue_t q, id block, PMKPromiseFulfiller resolver))mkpendingCallback 函数,一层层的调用,一层还没理解就下一次,无穷无尽了呢。不过没办法,只能接着往下看了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
- (
id
)resolved:(PMKResolveOnQueueBlock(^)(
id
result))mkresolvedCallback
pending:(
void
(^)(
id
result, PMKPromise *next, dispatch_queue_t q,
id
block, PMKPromiseFulfiller resolver))mkpendingCallback
{
__block PMKResolveOnQueueBlock callBlock;
__block
id
result;
dispatch_sync(_promiseQueue, ^{
if
((result = _result))
return
;
callBlock = ^(dispatch_queue_t q,
id
block) {
__block PMKPromise *next =
nil
;
// HACK we seem to expose some bug in ARC where this block can
// be an NSStackBlock which then gets deallocated by the time
// we get around to using it. So we force it to be malloc'd.
block = [block
copy
];
dispatch_barrier_sync(_promiseQueue, ^{
if
((result = _result))
return
;
__block PMKPromiseFulfiller resolver;
next = [PMKPromise
new
:^(PMKPromiseFulfiller fulfill, PMKPromiseRejecter reject) {
resolver = ^(
id
o){
if
(IsError(o)) reject(o);
else
fulfill(o);
};
}];
[_handlers addObject:^(
id
value){
mkpendingCallback(value, next, q, block, resolver);
}];
});
// next can still be `nil` if the promise was resolved after
// 1) `-thenOn` read it and decided which block to return; and
// 2) the call to the block.
return
next ?: mkresolvedCallback(result)(q, block);
};
});
// We could just always return the above block, but then every caller would
// trigger a barrier_sync on the promise queue. Instead, if we know that the
// promise is resolved (since that makes it immutable), we can return a simpler
// block that doesn't use a barrier in those cases.
return
callBlock ?: mkresolvedCallback(result);
}
|
我们看到dispatch_barrier_sync函数,等待该队列前面的操作任务执行完成后才会执行这个block块。这至关重要的函数保证了then的同步执行,它的block块作用如下,创建了一个新的PMKPromise并且把then中带的block添加到_handlers, 这两个操作是关键步骤,至于这个函数中得大多其他代码都是进行递归解析then方法(别忘了,这是一个链式调用方法)。
1
2
3
|
resolver = ^(
id
o){
if
(IsError(o)) reject(o);
else
fulfill(o);
};
|
这里我们需要最后跟进一步,看看new 方法中得fulfill是什么:
1
2
3
4
5
6
7
8
|
id
fulfiller = ^(
id
value){
if
(PMKGetResult(
this
))
return
NSLog
(@
"PromiseKit: Promise already resolved"
);
if
(IsError(value))
@throw
PMKE(@
"You may not fulfill a Promise with an NSError"
);
PMKResolve(
this
, value);
};
|
调用了PMKResolve函数,其他都是一些异常的处理。最后看看这个函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
static
void
PMKResolve(PMKPromise *
this
,
id
result) {
void
(^set)(
id
) = ^(
id
r){
NSArray
*handlers = PMKSetResult(
this
, r);
for
(
void
(^handler)(
id
) in handlers)
handler(r);
};
if
(IsPromise(result)) {
PMKPromise *next = result;
dispatch_barrier_sync(next->_promiseQueue, ^{
id
nextResult = next->_result;
if
(nextResult ==
nil
) {
// ie. pending
[next->_handlers addObject:^(
id
o){
PMKResolve(
this
, o);
}];
}
else
set(nextResult);
});
}
else
set(result);
}
|
执行所有的handler操作,并且设置result值。这里我们是否记得,在上面我们有把block存到handler中得。于是block就顺利的执行了。
PromiseKit 解析 (二) 递归 IOS
在PromiseKit中,里面涉及了很多的递归操作,而递归操作,对于大部分人理解起来都是比较麻烦的(包括我),而github中一些高上大的第三方库,无一不涉及了很漂亮的递归操作,因此理解递归操作是很必要的。如果想成为尽可能优秀的程序猿。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
dispatch_barrier_sync(_promiseQueue, ^{
if
((result = _result))
return
;
__block PMKPromiseFulfiller resolver;
next = [PMKPromise
new
:^(PMKPromiseFulfiller fulfill, PMKPromiseRejecter reject) {
resolver = ^(
id
o){
if
(IsError(o)) reject(o);
else
fulfill(o);
};
}];
[_handlers addObject:^(
id
value){
mkpendingCallback(value, next, q, block, resolver);
}];
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
static
void
PMKResolve(PMKPromise *
this
,
id
result) {
void
(^set)(
id
) = ^(
id
r){
NSArray
*handlers = PMKSetResult(
this
, r);
for
(
void
(^handler)(
id
) in handlers)
handler(r);
};
if
(IsPromise(result)) {
PMKPromise *next = result;
dispatch_barrier_sync(next->_promiseQueue, ^{
id
nextResult = next->_result;
if
(nextResult ==
nil
) {
// ie. pending
[next->_handlers addObject:^(
id
o){
PMKResolve(
this
, o);
}];
}
else
set(nextResult);
});
}
else
set(result);
}
|
dispatch_barrier_sync这个函数,保证了它是顺序执行的。那么它执行的顺序是不是就是类似于我们一行一行代码在执行一个个then中得block块了。至于值传递,我们也再上面说到了。这里就不在强调了。
最后递归比较难以理解,但是认真去解读的话还是可以的,在国外,很多高校都是以lisp或者haskell这样的函数式语言来接触编程的,有的有scheme这种语言的课程,这些语言的主要思想就是递归。因此递归还是在他们深深的脑海里的。许多情况下递归效率很高,代码很简洁。但是对于我们来说还是在不熟悉的情况下少用为妙。我也尝试学学这样一门语言,进行下头脑风暴。