promiseKit 解析 iOS


 

  最近在嘗試解決下載管理中,全部下載的速度問題,於是在github中試圖尋找答案,無意中發現了Promise這個第三方庫。它的作用如下
 
        
  PromiseKit is not just a  Promises implementation, it is also a collection of helper functions that make the typical asynchronous patterns we use in iOS development delightful too.
PromiseKit 不僅僅是 Promises的實現,它還是收集了一系列的有用的函數並且讓他們異步化,讓我們能愉快的進行IOS開發。 
 
這里提到了Promises 實現,我們看看你什么是promises : 
這里有相關參考鏈接:http://docs.scala-lang.org/overviews/core/futures.html
Futures provide a nice way to reason about performing many operations in parallel– in an efficient and non-blocking way. The idea is simple, a  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.
 
future 提供了一種漂亮的方式去論述並行操作的——有效率並且非阻塞的方式。思想很簡單,Future是一種占位對象,你可以為一個現在還沒有存在的東西創建它。總之,future的結果是在后來並行計算出來的。並行的任務,它讓結果獲得得更快。
 
以上描述是不是聽起來很有IOS中block塊的意思,只是它的操作是異步的。(目前至少我試這么理解的)
 
我們再來看看promises :
While futures are defined as a type of read-only placeholder object created for a result which doesn’t yet exist, a promise can be thought of as a writable, single-assignment container, which completes a future. That is, a promise can be used to successfully complete a future with a value (by “completing” the promise) using the success method. Conversely, a promise can also be used to complete a future with an exception, by failing the promise, using the failure method.
 
future定了了一種為現在還不存在的結果而創建的只讀得占位對象, promise 可以認為是可讀寫的,單一賦值的容器,它可以完成future。promise可以帶一個值成功完成future返回一個值,promise也可以完成future帶來異常。
 
promise 感覺就是管理future的一個對象。(常用的XXmanager ?_?)
 
廢話不多說,我們直接來看看這個第三方庫,廢話補多少,先上使用的例子: 
我們通常在發一個異步請求的時候一般會這么做:
 
@implementation MyViewController

- (void)viewDidLoad {
    id rq = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://placekitten.com/320/320"]];

    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

 

當我們使用promiseKit之后,代碼如下:
 
 
#import <PromiseKit.h>

- (void)viewDidLoad {
    [NSURLConnection GET:@"http://placekitten.com/320/320"].then(^(UIImage *image) {
        self.imageView.image = image;
    }).catch(^(NSError *error){
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:…];
        [alert promise].then(^{
            [self dismissViewControllerAnimated:YES];
        })
    });
}

 

 
        
是不是很神奇的說,呵呵...看起來有點高上大的樣子,簡介了不少代碼。看起來還是比較有誘惑性的代碼。
 
 下面我們正式來看看promiseKit(貌似前奏太多啦)。
  promiseKit 中,主要的類有一個.m文件就行了,其他都是關於常用UI或者請求方法的擴展。下面是目錄結構,主要的類用紅色標記出來了。是不是很簡單,其他都是擴張,你如果要用就加入,不用可以不需要加入你的項目當中。
 
我們看看Promise.h文件,下面是
 

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 代表了一個任務的未來的值, 

要獲得promise的值,調用then方法。當異步的任務解決成功,傳遞給then的block塊將會根據獲得的值執行,如果promise已經解決成功了當你then的時候,這個block塊將會立刻執行。
這個可以使用鏈式結果調用,then返回的值會成為下個then的中得值。
 
下面我們通過簡單的一個例子一步步跟進它里面的源碼,並且告訴你怎么用(再用發上面比較簡單,一下僅僅輕輕帶過使用方法,主要解釋它內部的實現):
    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。
- (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方法,這個是最主要的方法,如果這個方法理解清楚了,這個框架就可以舉一反三的大致理解清楚了。
- (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是異步操作的一個庫哦)

 
打斷了一下,我們繼續說thenOn函數,如下
- (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 函數,一層層的調用,一層還沒理解就下一次,無窮無盡了呢。不過沒辦法,只能接着往下看了。
- (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方法(別忘了,這是一個鏈式調用方法)。
最復雜的函數解決了,我們再返回去看看thenOn函數吧,由於then函數的主題是block,那么我們主要看thenOn,thenOn函數的后半部分,很簡單就兩步操作,首先進行了錯誤的處理,再調用resolve函數執行resolved:pending函數中得
resolver = ^(id o){
                          if (IsError(o)) reject(o); else fulfill(o);
                          };

 

這里我們需要最后跟進一步,看看new 方法中得fulfill是什么:
    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函數,其他都是一些異常的處理。最后看看這個函數
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就順利的執行了。
因此fulfiller才是執行block的真正方法。
 
關於UIAlertView的調用如下
貌似洋洋灑灑寫了這么多,有些亂,稍微整理下好了:
1.創建promise
2.調用then,then調用thenOn ,然后thenOn調用resolved:pending方法返回一個PMKPromise對象。
3.alertView:didDismissWithButtonIndex:方法調用了fulfiller.
 
調用過程還是很簡單的吧,我們了解了這么多,大體步驟也了解了。由於時間有限,我沒有詳細說明then是如何實現鏈式調用的,以后有時間補上。
總的來說這個類庫還是挺不錯的,在github上也有2000多得星了。如果大家喜歡可以研究下它。
 
我覺得:對於reactivecocoa覺得比較難用的,可以用這個先來熟悉下這個框架,畢竟沒有那么復雜,還有感覺這兩者還是思路和寫法上面還是比較類似的。
最后有什么不對的地方,希望大家多多指正!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM