iOS Bolts Framework 筆記

Bolts gitHub 連接地址

使用場(chǎng)景

當(dāng)程序啟動(dòng)的時(shí)候幽崩,我們根據(jù)需要慌申,向用戶展示相關(guān)界面,比如升級(jí)彈窗咨油,開啟推送彈窗柒爵,廣告彈窗棉胀,活動(dòng)彈窗等等霎挟,但是如果不去有效的管理這些數(shù)據(jù)氓扛,就會(huì)導(dǎo)致數(shù)據(jù)沖突论笔,界面混亂,從而給用戶造成一些困擾,因此本文應(yīng)運(yùn)而生了整份。

解決整個(gè) APP 運(yùn)行時(shí)串行執(zhí)行任務(wù)源碼如下
串行執(zhí)行任務(wù)

簡介

Bolts 是由 Facebook and Parse open source 的 Framework. 主要實(shí)現(xiàn)是為了解決 async callbacks 問題的 Promise Pattern. Promise / Future 在很多語言或 library 都有實(shí)現(xiàn)籽孙。 ( JQuery, AngularJS, Java, Dart ...etc )

雖說是 Framework 但用法并不難而且類數(shù)也不多犯建。主要是以 BFTask, BFTaskCompletionSource, BFExector 這三個(gè)為主要。

假設(shè)我們要建立一個(gè)testAsync function竿开,必須要在最后回傳 BFTask否彩,建立 task 的方式則使用BFTaskCompletionSource的靜態(tài)方法來建立 task 并設(shè)定相關(guān)參數(shù)

- (BFTask *)testAsync {
    NSLog(@"testAsync");
    BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
    source.result = @"test async complete";
    return source.task;
}

BFTaskCompletionSource是 BFTask 的proxy object(代理對(duì)象)嗦随,當(dāng) function 完成時(shí)設(shè)定 task.result枚尼,執(zhí)行后則視為task complete,task.result 是 id 動(dòng)態(tài)類型悬而,可以將處理完后需要的結(jié)果設(shè)定到task.result锭汛,function 執(zhí)行時(shí)導(dǎo)致的錯(cuò)誤失敗設(shè)定為task.error幕侠,如有執(zhí)行出現(xiàn)例外账锹,設(shè)定task.exception蔚袍,如要取消task,調(diào)用[task cancel]即可.

無論是 error/exception/cancel 執(zhí)行完后皆視為 task complete. 另外, task 提供了一些方法來判斷目前狀況 isCancelled / isCompleted / error / exception. 也可以直接使用 [BFTask taskWithResult] / [BFTask taskWithError] / [BFTask taskWithException] / [BFTask cancelledTask] 靜態(tài)方法直接建立 task 來做相關(guān)處理晋辆。

實(shí)現(xiàn) Async function 之后瓶佳,可以一個(gè)接著一個(gè)將要調(diào)用的函數(shù)串起來稱之為Chain鳞青,在每一個(gè)函數(shù)回調(diào)的 task 執(zhí)行continueWithBlockcontinueWithSuccessBlock

[[[[[self testAsync] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"%@",task.result);
        return [self testAsync1];
    }] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"%@",task.result);
        return [self testAsync2];
    }] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"%@",task.result);
        return [self testAsync3];
    }] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"%@",task.result);
        NSLog(@"task in chain complete");
        return nil;
    }];

另外還可以以Series / Parallel 的方式來執(zhí)行 Task

  • Series:以串行方式回調(diào)task執(zhí)行continueWithBlock
- (void)seriesTask {
    [[[self testAsync] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        BFTask *_task = [BFTask taskWithResult:nil];
        for (int i = 0; i < 3; i++) {
            task = [task continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
                return [self testAsync];
            }];
        }
        return _task;
    }] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"tasks execute in series complete");
        return nil;
    }];
}
  • Parallel:將所有 task 塞進(jìn)一個(gè) array后丟給taskForCompletionOfAllTasks處理
- (void)parallelTask {
    [[[self testAsync] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSMutableArray *tasks = [NSMutableArray array];
        for (int i = 0; i < 3; i++) {
            [tasks addObject:[self testAsync]];
        }
        [tasks addObject:[self testAsync]];
        return [BFTask taskForCompletionOfAllTasks:tasks];
    }] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"tasks execute in series complete");
        return nil;
    }];
}

BFTask主要是通過BFExecutor 來執(zhí)行,默認(rèn)是通過[BFExecutor defaultExexutor]器仗,另外還有immediateExecutor / mainThreadExecutor 可供使用童番,基于 GCD 實(shí)現(xiàn)。

  • immediateExecutor:是直接以GCD dispatch_once 執(zhí)行

  • mainThreadExecutor:以GCD dispatch_once 執(zhí)行轨香,檢查是否為isMainThread臂容,如果不是根蟹,則調(diào)用dispatch_async(dispatch_get_main_queue(),block);延后執(zhí)行

  • defaultExecutor:以GCD dispatch_once 執(zhí)行,會(huì)檢查current thread 的threadDictionary objectForKey:kBFTaskDepthKey索取出的 depth 是否超過20個(gè)球散,如果超過蕉堰,則dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); 延后執(zhí)行悲龟。否則會(huì)在當(dāng)前 Thread 將 depth + 1, try-catch 執(zhí)行完畢再 -1。

[[self testAsync] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id(BFTask *task) {
            NSLog(@"task execute");
            return nil;
}
例子如下所示
測(cè)試準(zhǔn)備

建立一個(gè) object 對(duì)象并聲明一個(gè)類方法返回 BFTask 對(duì)象
CSTask.h 文件

#import <Foundation/Foundation.h>
#import <Bolts/BFTaskCompletionSource.h>

@interface CSTask : NSObject

+ (BFTask *)taskWithResult:(NSString *)result;

@end

CSTask.m 實(shí)現(xiàn)文件

#import "CSTask.h"

@implementation CSTask

+ (BFTask *)taskWithResult:(NSString *)result {
    BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
    source.result = result;
    return source.task;
}

@end

編寫測(cè)試方法

- (BFTask *)testAsync {
    NSLog(@"testAsync");
    BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
    source.result = @"test async complete";
    return source.task;
}

- (BFTask *)testAsync1 {
    NSLog(@"testAsync1");
    return [CSTask taskWithResult:@"test async1 complete"];
}

- (BFTask *)testAsync2 {
    NSLog(@"testAsync2");
    return [CSTask taskWithResult:@"test async2 complete"];
}

- (BFTask *)testAsync3 {
    NSLog(@"testAsync3");
    return [CSTask taskWithResult:@"test async3 complete"];
}

串行執(zhí)行文件

- (void)testTask {
    [[[[[self testAsync] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"%@",task.result);
        return [self testAsync1];
    }] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"%@",task.result);
        return [self testAsync2];
    }] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"%@",task.result);
        return [self testAsync3];
    }] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"%@",task.result);
        NSLog(@"task in chain complete");
        return nil;
    }];
}

執(zhí)行結(jié)果

image.png
測(cè)試串行
- (void)seriesTask {
    [[[self testAsync] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        BFTask *_task = [BFTask taskWithResult:nil];
        for (int i = 0; i < 3; i++) {
            task = [task continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
                return [self testAsync1];
            }];
        }
        return _task;
    }] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"tasks execute in series complete");
        NSLog(@"%@",task.result);
        return nil;
    }];
}

運(yùn)行結(jié)果

image.png
測(cè)試并行
- (void)parallelTask {
    [[[self testAsync] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSMutableArray *tasks = [NSMutableArray array];
        for (int i = 0; i < 3; i++) {
            [tasks addObject:[self testAsync1]];
        }
        [tasks addObject:[self testAsync2]];
        return [BFTask taskForCompletionOfAllTasks:tasks];
    }] continueWithBlock:^id _Nullable(BFTask * _Nonnull task) {
        NSLog(@"tasks execute in series complete");
        NSLog(@"%@",task.result);
        return nil;
    }];
}

運(yùn)行結(jié)果

image.png
項(xiàng)目實(shí)戰(zhàn)

一般我們需要在 APP 啟動(dòng)后依次彈一些彈窗進(jìn)行交互划乖,比如版本更新提示诀拭,活動(dòng)廣告等等煤蚌, 所以保證彈窗依次彈出并且不會(huì)重疊在一起就顯得很重要了。

詳情見范例

  • StartupSerialTasks.h
#import <Foundation/Foundation.h>
#import <Bolts/BFTask.h>
#import <Bolts/BFTaskCompletionSource.h>

@interface StartupSerialTasks : NSObject

+ (StartupSerialTasks *)instance;

// 程序啟動(dòng)時(shí)需要做的事情
- (void)addStartupTask:(NSString *)key withBlock:(void (^)(BFTaskCompletionSource *promise))block;

@end
  • StartupSerialTasks.m
@implementation StartupSerialTasks {
    BFTask *_chainStartupTask;  // 程序啟動(dòng)時(shí)需要做的事情
}

+ (StartupSerialTasks *)instance {
    static StartupSerialTasks *_instance = nil;
    @synchronized (self) {
        if (_instance == nil) {
            _instance = [[self alloc] init];
        }
    }
    return _instance;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _chainStartupTask = [BFTask taskWithResult:@"程序啟動(dòng)執(zhí)行的任務(wù)"];
    }
    return self;
}

// 程序啟動(dòng)時(shí)需要做的事情
- (void)addStartupTask:(NSString *)key withBlock:(void (^)(BFTaskCompletionSource *promise))block {
    _chainStartupTask = [_chainStartupTask continueWithBlock:^id _Nullable(BFTask *task) {
        BFTaskCompletionSource *asyncRes = [BFTaskCompletionSource taskCompletionSource];
        // 如果調(diào)用的是在主線程,這里則回調(diào)回主線程調(diào)用.
        dispatch_async([NSThread isMainThread] ? dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0) : dispatch_get_main_queue(), ^{
            block(asyncRes);
        });
        return asyncRes.task;
    }];
}

@end

外界使用:假設(shè)我們要依次執(zhí)行5個(gè)任務(wù),每個(gè)任務(wù)的執(zhí)行時(shí)間不確定翰苫,要求任務(wù)按照先后順序依次執(zhí)行这橙。

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 一般解決程序啟動(dòng),依次顯示彈窗任務(wù)
    for (int i = 0; i < 5; i++) {
        [self startupTask:[NSString stringWithFormat:@"任務(wù)%d",i]];
    }
}

// 開始執(zhí)行任務(wù)
- (void)startupTask:(NSString *)name {
    [[StartupSerialTasks instance] addStartupTask:@"windowShow" withBlock:^(BFTaskCompletionSource *promise) {
        NSUInteger time = arc4random_uniform(5);
        NSLog(@"%@",[NSString stringWithFormat:@"開始執(zhí)行%@,執(zhí)行時(shí)間:%lu",name,time]);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",[NSString stringWithFormat:@"%@執(zhí)行完畢",name]);
            [promise trySetResult:@YES];
        });
    }];
}

運(yùn)行結(jié)果

image.png

通過打印結(jié)果可知埃唯,任務(wù)一到任務(wù)五依次執(zhí)行鹰晨,執(zhí)行時(shí)間不固定,嚴(yán)格按照指定的順序執(zhí)行漠趁。

任務(wù)執(zhí)行完畢一定要將其設(shè)置為完成忍疾,[promise trySetResult:@YES]否則下一個(gè)任務(wù)不會(huì)接著執(zhí)行。


本文參考 kakukeme 的 Bolts framework iOS 筆記丸边,非常感謝該作者荚孵。


項(xiàng)目連接地址 - Bolts

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末收叶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蜓萄,更是在濱河造成了極大的恐慌,老刑警劉巖辟犀,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堂竟,死亡現(xiàn)場(chǎng)離奇詭異玻佩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)税稼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門郎仆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兜蠕,“玉大人,你說我怎么就攤上這事狡耻『锇迹” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長书劝。 經(jīng)常有香客問我,道長猾昆,這世上最難降的妖魔是什么骡苞? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮烘苹,結(jié)果婚禮上片部,老公的妹妹穿的比我還像新娘。我一直安慰自己廊鸥,他們只是感情好站粟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布奴烙。 她就那樣靜靜地躺著剖张,像睡著了一般搔弄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上倒庵,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天炫刷,我揣著相機(jī)與錄音浑玛,去河邊找鬼。 笑死顾彰,一個(gè)胖子當(dāng)著我的面吹牛涨享,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拆又,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼栈源!你這毒婦竟也來了竖般?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挣郭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侄非,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逞怨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年叠赦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了革砸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖笔时,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情借笙,我是刑警寧澤较锡,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布蚂蕴,位于F島的核電站俯邓,受9級(jí)特大地震影響稽鞭,放射性物質(zhì)發(fā)生泄漏引镊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一吩抓、第九天 我趴在偏房一處隱蔽的房頂上張望赴恨。 院中可真熱鬧伦连,春花似錦、人聲如沸除师。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽忍宋。三九已至,卻和暖如春糠排,著一層夾襖步出監(jiān)牢的瞬間入宦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來泰國打工落追, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涯肩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓症汹,卻偏偏與公主長得像贷腕,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芽世,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,395評(píng)論 8 265
  • NSThread 第一種:通過NSThread的對(duì)象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 801評(píng)論 0 3
  • 1.NSTimer不準(zhǔn)時(shí)的原因:(1).RunLoop循環(huán)處理時(shí)間妹卿,每次循環(huán)是固定時(shí)間,只有在這段時(shí)間才會(huì)去查看N...
    稻春閱讀 1,245評(píng)論 0 3
  • 沒有一點(diǎn)點(diǎn)防備箕宙,也沒有一絲顧慮铺纽。你就這樣的出現(xiàn),在我的世界里狡门,帶給我驚喜其馏,情不自已。來來來叛复,掌聲歡迎一下親戚,姨媽...
    賀小妮閱讀 9,477評(píng)論 110 79
  • 自己是個(gè)理科生,腦子里多的是數(shù)字和邏輯抖僵,少的是修辭和漫想。一直認(rèn)為自己不適合寫文章义桂,不適合去做一些和文章有關(guān)...
    亦小木閱讀 204評(píng)論 0 0