iOS 多線程N(yùn)SOperation

一. 定義

NSOperation是蘋果公司提供的一套多線程解決方案, 它是基于GCD 的更抽象的"面向?qū)ο?封裝.

二. 對比GCD

  • 支持任務(wù)之間添加依賴關(guān)系, 控制執(zhí)行順序
  • 提供可選實現(xiàn)的任務(wù)完成block
  • 可使用KVO監(jiān)聽任務(wù)執(zhí)行狀態(tài)變化
  • 支持設(shè)置任務(wù)的優(yōu)先級, 可控制任務(wù)的相對執(zhí)行順序
  • 提供取消操作

三. 任務(wù)

任務(wù)就是在線程中執(zhí)行的代碼, 在GCD中表現(xiàn)形式是block, NSOperation 是在其子類NSInvocationOperation, NSBlockOperation, 自定義子類中執(zhí)行任務(wù)代碼.

四. 隊列

NSOperationQueue表示隊列, 用來存放任務(wù)的隊列

  • NSOperation單獨使用時是同步操作, 配合NSOperationQueue才能實現(xiàn)異步操作
  • GCD添加到隊列中的任務(wù)遵守FIFO原則, 對于添加到NSOperationQueue中的任務(wù), 首先根據(jù)任務(wù)之間的依賴關(guān)系決定任務(wù)的就緒狀態(tài), 進(jìn)入就緒狀態(tài)的任務(wù)由任務(wù)之間的相對優(yōu)先級決定執(zhí)行順序
  • NSOperationQueue可以設(shè)置最大并發(fā)任務(wù)數(shù)量
  • NSOperationQueue 提供兩種隊列: 主隊列和自定義隊列,
    • 主隊列在主線程運(yùn)行
    • 自定義隊列在后臺執(zhí)行

五. 基本使用

NSOperation是一個抽象類, 使用的時候必須子類化, 提供三種方式創(chuàng)建任務(wù):

1. 使用子類NSInvocationOperation
NSInvocationOperation *invocationOpeartion = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOpeartion) object:nil];
    
// 如果沒有添加到 NSOperationQueue, 則需要手動調(diào)用 start
[invocationOpeartion start];

/// 回調(diào)方法
- (void)invocationOpeartion {
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}

注意: NSInvocationOperation單獨使用時, 并沒有開啟新的線程, 任務(wù)都是在當(dāng)前線程中執(zhí)行

2. 使用子類NSBlockOperation
  • 2.1 簡單使用
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        // 任務(wù)代碼
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}];
    
[blockOperation start];

注意: NSBlockOperation單獨使用時, 并沒有開啟新的線程, 任務(wù)都是在當(dāng)前線程中執(zhí)行

  • 2.2: NSBlockOperation類有一個實例方法addExecutionBlock, 這個方法可以添加一個代碼塊, 當(dāng)執(zhí)行NSBlockOperation對象 start 時, 該對象將其所有塊提交給默認(rèn)優(yōu)先級的并發(fā)調(diào)度隊列. 然后對象等待所有的塊完成執(zhí)行. 當(dāng)最后一個塊完成執(zhí)行時, 操作對象將自己標(biāo)記為已完成. 由于塊操作本身在單獨的線程上運(yùn)行, 所以應(yīng)用程序的其他線程可以在等待塊操作完成的同時繼續(xù)工作.
    需要說明的一點是, 如果添加的任務(wù)較多的話, 這些操作(包括blockOperationWithBlock中的操作)可能在不同的線程中并發(fā)執(zhí)行, 這是由系統(tǒng)決定的
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    // 任務(wù)代碼
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}];
    
[blockOperation addExecutionBlock:^{
    NSLog(@"addExecutionBlock1:%@", NSThread.currentThread);
}];
[blockOperation addExecutionBlock:^{
    NSLog(@"addExecutionBlock2:%@", NSThread.currentThread);
}];
[blockOperation addExecutionBlock:^{
    NSLog(@"addExecutionBlock3:%@", NSThread.currentThread);
}];
[blockOperation start];

在調(diào)用了addExecutionBlock方法添加了多組任務(wù)后技俐,開啟新的線程,任務(wù)是并發(fā)執(zhí)行的,blockOperationWithBlock中的任務(wù)執(zhí)行是否在當(dāng)前的線程執(zhí)行, 統(tǒng)一由系統(tǒng)調(diào)度.

3. 自定義繼承自NSOperation的子類
@interface Operation : NSOperation

@end

@implementation Operation

-(void)main {
    if (!self.isCancelled) {
        for (int i = 0; i < 4; i++) {
            sleep(2);
            NSLog(@"%d==%@", i, NSThread.currentThread);
        }
    }
}

@end

// 使用:
Operation *op = [[Operation alloc] init];
[op start];

注意: 自定義的Operation并沒有開啟新的線程,任務(wù)的執(zhí)行是在當(dāng)前的線程中執(zhí)行的。

六. 隊列: NSOperationQueue

NSOperationQueue提供了主隊列和自定義隊列

  • 主隊列: [NSOperationQueue mainQueue];, 凡是添加到主隊列的NSOperation任務(wù)都會放到主線程執(zhí)行
  • 自定義隊列: [[NSOperationQueue alloc] init], 凡是添加到自定義隊列的NSOperation任務(wù)都會放到子線程執(zhí)行.
1.添加任務(wù)到隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

// 方式1: 
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    // 任務(wù)代碼
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}];
[queue addOperation:blockOperation];

// 方式2: 
[queue addOperationWithBlock:^{
    // 任務(wù)代碼
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}];


// 注意: 不需要調(diào)用NSBlockOperationstart方法

2.添加多個操作到隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

// 操作1
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    // 任務(wù)代碼
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}];

// 操作2
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];

// 方式1:
[queue addOperation:blockOperation];
[queue addOperation:invocationOperation];

// 方式2:
// waitUntilFinished參數(shù)祖今,如果傳YES,則表示會等待隊列里面的任務(wù)執(zhí)行完成后才會往下執(zhí)行,也就是會阻塞線程
[queue addOperations:@[blockOperation, invocationOperation] waitUntilFinished:true];

/// 回調(diào)方法
- (void)invocationOperation {
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
}

總結(jié): waitUntilFinished參數(shù)击你,如果傳YES,則表示會等待隊列里面的任務(wù)執(zhí)行完成后才會往下執(zhí)行谎柄,也就是會阻塞線程

七. 同步&并發(fā)

NSOperationQueue有個一屬性maxConcurrentOperationCount
maxConcurrentOperationCount 默認(rèn)-1, 不做限制, 在子線程中執(zhí)行任務(wù)
maxConcurrentOperationCount = 1, 隊列中的任務(wù)會同步執(zhí)行, 阻塞當(dāng)前線程
maxConcurrentOperationCount > 1, 隊列中的任務(wù)會并發(fā)執(zhí)行, 開啟子線程執(zhí)行
maxConcurrentOperationCount值并不是表示并發(fā)執(zhí)行的線程數(shù)量丁侄,而是在一個隊列中能夠同時執(zhí)行的任務(wù)的數(shù)量。

八. NSOperation線程間的通訊, 子線程完成任務(wù), 會主線程更新UI

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    // 任務(wù)代碼
    for (int i = 0; i < 5; i++) {
        sleep(2);
        NSLog(@"%d -- %@", i, NSThread.currentThread);
    }
    // 回主隊列更新UI
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        NSLog(@"更新UI");
    }];
}];
[queue addOperation:blockOperation];

九. 任務(wù)的依賴關(guān)系

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    // 任務(wù)代碼
    NSLog(@"1");
    
}];

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"2");
}];

NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"3");
}];

// 1依賴于2和3, 意思是在2和3都執(zhí)行完后才會執(zhí)行1;
[operation1 addDependency:operation2];
[operation1 addDependency:operation3];
// 將1的優(yōu)先級設(shè)置最高
operation1.queuePriority = NSOperationQueuePriorityHigh;

[queue addOperations:@[operation1, operation2, operation3] waitUntilFinished:NO];
//log: 2 3 1

總結(jié):
queuePriority不能取代依賴關(guān)系
queuePriority屬性只對同一個隊列有效

十.設(shè)置完成回調(diào)

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    // 任務(wù)代碼
    NSLog(@"1");
}];

// 設(shè)置任務(wù)1完成的回調(diào)block
[operation1 setCompletionBlock:^{
        NSLog(@"任務(wù)1完成了");
}];

十一. 監(jiān)聽狀態(tài)

  • 使用KVO觀察對operation狀態(tài)的監(jiān)聽: isExcuting, isFinished, isCancelled.
  • isExcuting 正在執(zhí)行任務(wù)
  • isFinished 任務(wù)完成
  • isCancelled 任務(wù)取消執(zhí)行
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谷誓,一起剝皮案震驚了整個濱河市绒障,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌捍歪,老刑警劉巖户辱,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異糙臼,居然都是意外死亡庐镐,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門变逃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來必逆,“玉大人,你說我怎么就攤上這事揽乱∶迹” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵凰棉,是天一觀的道長损拢。 經(jīng)常有香客問我,道長撒犀,這世上最難降的妖魔是什么福压? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任掏秩,我火速辦了婚禮,結(jié)果婚禮上荆姆,老公的妹妹穿的比我還像新娘蒙幻。我一直安慰自己,他們只是感情好胆筒,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布邮破。 她就那樣靜靜地躺著,像睡著了一般仆救。 火紅的嫁衣襯著肌膚如雪决乎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天派桩,我揣著相機(jī)與錄音构诚,去河邊找鬼。 笑死铆惑,一個胖子當(dāng)著我的面吹牛范嘱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播员魏,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼丑蛤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了撕阎?” 一聲冷哼從身側(cè)響起受裹,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎虏束,沒想到半個月后棉饶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡镇匀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年照藻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汗侵。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡幸缕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出晰韵,到底是詐尸還是另有隱情发乔,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布雪猪,位于F島的核電站栏尚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏浪蹂。R本人自食惡果不足惜抵栈,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坤次。 院中可真熱鬧古劲,春花似錦、人聲如沸缰猴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滑绒。三九已至闷堡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疑故,已是汗流浹背杠览。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留纵势,地道東北人踱阿。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像钦铁,于是被迫代替她去往敵國和親软舌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

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