iOS多線程(三):NSOperationQueue 的使用

1 簡(jiǎn)單使用 NSOperationQueue

上一篇文章中看到使用自定義NSOperation來(lái)實(shí)現(xiàn)多線程靠柑,寫(xiě)法有些復(fù)雜,但其實(shí)吓懈,使用NSOperationQueue來(lái)實(shí)現(xiàn)多線程非常簡(jiǎn)單

- (void)viewDidLoad {
    [super viewDidLoad];
    // 創(chuàng)建3個(gè) NSInvocationOperation 操作
    NSOperationQueue *opQueue = [NSOperationQueue new];
    for (NSUInteger i = 0; i < 3; i++) {
        // 可以傳遞一個(gè) NSObject 給operation的操作方法
        NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Operation_%lu", i] forKey:@"key"];
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationSelector:) object:dict];
        [opQueue addOperation:op];
    }
}
    
// NSInvocationOperation 操作執(zhí)行的方法
- (void)operationSelector:(NSDictionary *)dict
{
    // 接收傳進(jìn)來(lái)的dict
    NSLog(@"dictValue = %@", [dict valueForKey:@"key"]);
    sleep(10);  // 加個(gè)睡眠模仿耗時(shí)操作
    NSLog(@"currentThread = %@", [NSThread currentThread]);
    NSLog(@"mainThread    = %@", [NSThread mainThread]);
}

控制臺(tái)輸出結(jié)果為:

2016-02-25 16:58:18.282 test[57194:18487531] dictValue = Operation_0
2016-02-25 16:58:18.282 test[57194:18487530] dictValue = Operation_1
2016-02-25 16:58:18.282 test[57194:18487533] dictValue = Operation_2
2016-02-25 16:58:28.283 test[57194:18487530] currentThread = <NSThread: 0x7fbf40435bb0>{number = 2, name = (null)}
2016-02-25 16:58:28.284 test[57194:18487531] currentThread = <NSThread: 0x7fbf4050f2a0>{number = 3, name = (null)}
2016-02-25 16:58:28.284 test[57194:18487533] currentThread = <NSThread: 0x7fbf4290f560>{number = 4, name = (null)}
2016-02-25 16:58:28.284 test[57194:18487530] mainThread    = <NSThread: 0x7fbf405058c0>{number = 1, name = (null)}
2016-02-25 16:58:28.284 test[57194:18487531] mainThread    = <NSThread: 0x7fbf405058c0>{number = 1, name = (null)}
2016-02-25 16:58:28.284 test[57194:18487533] mainThread    = <NSThread: 0x7fbf405058c0>{number = 1, name = (null)}

可以看出來(lái)這三個(gè)操作是并發(fā)執(zhí)行的歼冰,而且都不在主線程中執(zhí)行。

2 NSOperationQueue 的其他屬性

添加操作有3個(gè)方法:

// 直接添加一個(gè) NSOperation 操作耻警,并且加入并發(fā)隊(duì)列隔嫡,只要當(dāng)前隊(duì)列允許,就會(huì)立刻執(zhí)行甘穿。
- (void)addOperation:(NSOperation *)op;
// 添加一組操作腮恩,如果 waitUntilFinished 為 NO,則必須在當(dāng)前隊(duì)列中的所有操作都執(zhí)行完了温兼,才會(huì)執(zhí)行這組操作秸滴,否則立刻執(zhí)行。
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);
// 直接在這里寫(xiě)一個(gè)block募判,block中的操作加入并發(fā)隊(duì)列荡含,并且只要當(dāng)前隊(duì)列允許執(zhí)行,就會(huì)立刻執(zhí)行届垫。
- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);

接下來(lái)看其他的屬性

// 返回當(dāng)前隊(duì)列中的所有操作NSOperation
@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;
// 返回當(dāng)前隊(duì)列中的操作數(shù)量内颗,對(duì)應(yīng) operations.count
@property (readonly) NSUInteger operationCount NS_AVAILABLE(10_6, 4_0);
// 可讀寫(xiě)的屬性,當(dāng)設(shè)備性能不足或根據(jù)需求要限制并行的操作數(shù)量時(shí)敦腔,可以設(shè)置這個(gè)值均澳。
// 設(shè)置了這個(gè)值之后,隊(duì)列中并發(fā)執(zhí)行的操作數(shù)量不會(huì)大于這個(gè)值符衔。超出這個(gè)值在排隊(duì)中的操作會(huì)處于休眠狀態(tài)找前。
// 默認(rèn)值為 NSOperationQueueDefaultMaxConcurrentOperationCount = -1
@property NSInteger maxConcurrentOperationCount;
// 可以給隊(duì)列指定一個(gè)名字用來(lái)做標(biāo)識(shí)
@property (nullable, copy) NSString *name NS_AVAILABLE(10_6, 4_0);
// 給隊(duì)列指定一個(gè)優(yōu)先級(jí),默認(rèn)為 NSQualityOfServiceDefault = -1
@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);
// ??? 這個(gè)不是太理解
@property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue NS_AVAILABLE(10_10, 8_0);
// 取消隊(duì)列中的所有操作判族。其實(shí)就是調(diào)用 operations 中每個(gè)操作的`cancel`方法才取消操作躺盛。
// 但是,在前面的文章中說(shuō)過(guò)形帮,調(diào)用`cancel`方法并不會(huì)終止操作槽惫,而是設(shè)置`cancelled`屬性為 YES,
// 這就需要自己在操作中分節(jié)點(diǎn)去判斷`cancelled`屬性了辩撑,在適當(dāng)?shù)臅r(shí)機(jī)結(jié)束操作界斜。
- (void)cancelAllOperations;
// 調(diào)用這個(gè)方法時(shí),會(huì)判斷 NSOperationQueue 中的操作是否全部執(zhí)行完合冀,如果沒(méi)有各薇,則調(diào)用者所在的線程會(huì)在調(diào)用處等待。
// 直到 NSOperationQueue 中的所有操作執(zhí)行完成君躺,當(dāng)前線程才繼續(xù)執(zhí)行峭判。如果 NSOperationQueue 為空开缎,則該方法立刻返回。
- (void)waitUntilAllOperationsAreFinished;
// 取得調(diào)用者的當(dāng)前線程中的 NSOperationQueue 操作隊(duì)列
+ (nullable NSOperationQueue *)currentQueue NS_AVAILABLE(10_6, 4_0);
// 取得主線程中的 
+ (NSOperationQueue *)mainQueue NS_AVAILABLE(10_6, 4_0);
@property (getter=isSuspended) BOOL suspended;

這個(gè)值很有意思林螃,從字面意思理解是暫停隊(duì)列奕删,但是怎么個(gè)暫停呢?從官方文檔上看

**Discussion**
When the value of this property is NO, the queue actively starts operations that are in the queue and ready to execute. Setting this property to YES prevents the queue from starting any queued operations, but already executing operations continue to execute. You may continue to add operations to a queue that is suspended but those operations are not scheduled for execution until you change this property to NO.

Operations are removed from the queue only when they finish executing. However, in order to finish executing, an operation must first be started. Because a suspended queue does not start any new operations, it does not remove any operations (including cancelled operations) that are currently queued and not executing.

You may monitor changes to the value of this property using key-value observing. Configure an observer to monitor the suspended key path of the operation queue.

The default value of this property is NO.

大概翻譯一下疗认,如果這個(gè)值設(shè)置為 NO完残,那說(shuō)明這個(gè)隊(duì)列已經(jīng)準(zhǔn)備好了可以執(zhí)行了。如果這個(gè)值設(shè)置為 YES侮邀,那么已經(jīng)添加到隊(duì)列中的操作還是可以執(zhí)行了坏怪,而后面繼續(xù)添加進(jìn)隊(duì)列中的操作才處于暫停狀態(tài),直到你再次將這個(gè)值設(shè)置為 NO 時(shí)绊茧,后面加入的操作才會(huì)繼續(xù)執(zhí)行铝宵。這個(gè)屬性的默認(rèn)值是 NO。

來(lái)看一下使用的方法例子:

- (void)viewDidLoad {
    [super viewDidLoad];
    // 創(chuàng)建3個(gè) NSInvocationOperation 操作
    _opQueue = [NSOperationQueue new];
    for (NSUInteger i = 0; i < 3; i++) {
        // 可以傳遞一個(gè) NSObject 給operation的操作方法
        NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Operation_%lu", i] forKey:@"key"];
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationSelector:) object:dict];
        [_opQueue addOperation:op];
    }
    // 這里設(shè)置為 YES
    _opQueue.suspended = YES;
    // 然后再添加一個(gè)操作华畏,序號(hào)為 9
    NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Operation_%d", 9] forKey:@"key"];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationSelector:) object:dict];
    [_opQueue addOperation:op];
}
// NSInvocationOperation 操作執(zhí)行的方法
- (void)operationSelector:(NSDictionary *)dict
{
    // 接收傳進(jìn)來(lái)的dict
    NSLog(@"dictValue = %@", [dict valueForKey:@"key"]);
    sleep(10);  // 加個(gè)睡眠模仿耗時(shí)操作
    NSLog(@"currentThread = %@", [NSThread currentThread]);
    NSLog(@"mainThread    = %@", [NSThread mainThread]);
    // 執(zhí)行完其中一個(gè)操作之后把 suspended 改為 NO鹏秋。
    _opQueue.suspended = NO;
}
2016-02-25 17:22:07.546 test[57547:18605364] dictValue = Operation_2
2016-02-25 17:22:07.546 test[57547:18605360] dictValue = Operation_0
2016-02-25 17:22:07.546 test[57547:18605361] dictValue = Operation_1
2016-02-25 17:22:10.547 test[57547:18605361] currentThread = <NSThread: 0x7ff598d07b00>{number = 3, name = (null)}
2016-02-25 17:22:10.547 test[57547:18605364] currentThread = <NSThread: 0x7ff59a9784f0>{number = 2, name = (null)}
2016-02-25 17:22:10.547 test[57547:18605360] currentThread = <NSThread: 0x7ff59aa05100>{number = 4, name = (null)}
2016-02-25 17:22:10.547 test[57547:18605364] mainThread    = <NSThread: 0x7ff598c08770>{number = 1, name = (null)}
2016-02-25 17:22:10.547 test[57547:18605360] mainThread    = <NSThread: 0x7ff598c08770>{number = 1, name = (null)}
2016-02-25 17:22:10.547 test[57547:18605361] mainThread    = <NSThread: 0x7ff598c08770>{number = 1, name = (null)}
2016-02-25 17:22:10.547 test[57547:18605513] dictValue = Operation_9
2016-02-25 17:22:13.620 test[57547:18605513] currentThread = <NSThread: 0x7ff598c08ce0>{number = 5, name = (null)}
2016-02-25 17:22:13.620 test[57547:18605513] mainThread    = <NSThread: 0x7ff598c08770>{number = 1, name = (null)}

可以看出來(lái),操作9是在suspended改為 NO 之后才開(kāi)始執(zhí)行的亡笑。

最后:以上很多屬性都支持 KVO 侣夷,可以通過(guò)監(jiān)聽(tīng)某個(gè)值的變化來(lái)做不同的操作,這里就不贅述了仑乌。

3 總結(jié)

NSOperationQueue為我們提供了非常簡(jiǎn)便的使用多線程的方法百拓,如果需要使用NSOperation,則更多建議使用NSOperationQueue而不是自定義NSOperation晰甚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末衙传,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子厕九,更是在濱河造成了極大的恐慌蓖捶,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扁远,死亡現(xiàn)場(chǎng)離奇詭異俊鱼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)畅买,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)并闲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人皮获,你說(shuō)我怎么就攤上這事焙蚓。” “怎么了洒宝?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵购公,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我雁歌,道長(zhǎng)宏浩,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任靠瞎,我火速辦了婚禮比庄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘乏盐。我一直安慰自己佳窑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布父能。 她就那樣靜靜地躺著神凑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪何吝。 梳的紋絲不亂的頭發(fā)上溉委,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音爱榕,去河邊找鬼瓣喊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛黔酥,可吹牛的內(nèi)容都是我干的藻三。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼跪者,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼棵帽!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起坑夯,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤岖寞,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后柜蜈,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體仗谆,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年淑履,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了隶垮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡秘噪,死狀恐怖狸吞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤蹋偏,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布便斥,位于F島的核電站,受9級(jí)特大地震影響威始,放射性物質(zhì)發(fā)生泄漏枢纠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一黎棠、第九天 我趴在偏房一處隱蔽的房頂上張望晋渺。 院中可真熱鬧,春花似錦脓斩、人聲如沸木西。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)八千。三九已至,卻和暖如春挪挤,著一層夾襖步出監(jiān)牢的瞬間叼丑,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工扛门, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸠信,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓论寨,卻偏偏與公主長(zhǎng)得像星立,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子葬凳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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