NSOperation和NSOperationQueue

NSOperation和NSOperationQueue

NSOperation 是蘋(píng)果公司對(duì) GCD 的封裝梅猿,完全面向?qū)ο蟛郯溃允褂闷饋?lái)更好理解爷辱。 大家可以看到 NSOperationNSOperationQueue 分別對(duì)應(yīng) GCD 的 任務(wù) 和 隊(duì)列 鳄乏。操作步驟也很好理解:

  1. 將要執(zhí)行的任務(wù)封裝到一個(gè) NSOperation 對(duì)象中唠亚。
  2. 將此任務(wù)添加到一個(gè) NSOperationQueue 對(duì)象中蚪战。

然后系統(tǒng)就會(huì)自動(dòng)在執(zhí)行任務(wù)牵现。至于同步還是異步、串行還是并行請(qǐng)繼續(xù)往下看:

添加任務(wù)

值得說(shuō)明的是邀桑,NSOperation 只是一個(gè)抽象類(lèi)瞎疼,所以不能封裝任務(wù)。但它有 2 個(gè)子類(lèi)用于封裝任務(wù)壁畸。分別是:NSInvocationOperationNSBlockOperation 贼急。創(chuàng)建一個(gè) Operation 后,需要調(diào)用 start 方法來(lái)啟動(dòng)任務(wù)捏萍,它會(huì) 默認(rèn)在當(dāng)前隊(duì)列同步執(zhí)行太抓。當(dāng)然你也可以在中途取消一個(gè)任務(wù),只需要調(diào)用其 cancel 方法即可令杈。

  • NSInvocationOperation : 需要傳入一個(gè)方法名走敌。
    OBJECTIVE-C
      //1.創(chuàng)建NSInvocationOperation對(duì)象
      NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

      //2.開(kāi)始執(zhí)行
      [operation start];
SWIFT

在 `Swift` 構(gòu)建的和諧社會(huì)里,是容不下 NSInvocationOperation 這種不是類(lèi)型安全的敗類(lèi)的逗噩。蘋(píng)果如是說(shuō)悔常。這里有[相關(guān)解釋](http://stackoverflow.com/questions/26644477/nsinvocationoperation-is-unavailable-in-xcode-6-1)
  • NSBlockOperation
    OBJECTIVE-C
      //1.創(chuàng)建NSBlockOperation對(duì)象
      NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
          NSLog(@"%@", [NSThread currentThread]);
      }];

      //2.開(kāi)始任務(wù)
      [operation start];
SWIFT
      //1.創(chuàng)建NSBlockOperation對(duì)象
      let operation = NSBlockOperation { () -> Void in
          println(NSThread.currentThread())
      }

      //2.開(kāi)始任務(wù)
      operation.start()
之前說(shuō)過(guò)這樣的任務(wù),默認(rèn)會(huì)在當(dāng)前線程執(zhí)行给赞。但是 `NSBlockOperation` 還有一個(gè)方法:`addExecutionBlock:` 机打,通過(guò)這個(gè)方法可以給 `Operation` 添加多個(gè)執(zhí)行 `Block`。這樣 `Operation` 中的任務(wù) 會(huì)**并發(fā)執(zhí)行**片迅,它會(huì) 在**主線程**和**其它的多個(gè)線程** 執(zhí)行這些任務(wù)残邀,注意下面的打印結(jié)果:
OBJECTIVE-C
          //1.創(chuàng)建NSBlockOperation對(duì)象
          NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
              NSLog(@"%@", [NSThread currentThread]);
          }];

          //添加多個(gè)Block
          for (NSInteger i = 0; i < 5; i++) {
              [operation addExecutionBlock:^{
                  NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
              }];
          }

          //2.開(kāi)始任務(wù)
          [operation start];
SWIFT
            //1.創(chuàng)建NSBlockOperation對(duì)象
            let operation = NSBlockOperation { () -> Void in
                NSLog("%@", NSThread.currentThread())
            }

            //2.添加多個(gè)Block
            for i in 0..<5 {
                operation.addExecutionBlock { () -> Void in
                    NSLog("第%ld次 - %@", i, NSThread.currentThread())
                }
            }

            //2.開(kāi)始任務(wù)
            operation.start()
打印輸出
2017-04-11 17:50:16.585 test[17527:4095467] 第2次 - <NSThread: 0x7ff5c9701910>{number = 1, name = main}

2017-04-11 17:50:16.585 test[17527:4095666] 第1次 - <NSThread: 0x7ff5c972caf0>{number = 4, name = (null)}

2017-04-11 17:50:16.585 test[17527:4095665] <NSThread: 0x7ff5c961b610>{number = 3, name = (null)}

2017-04-11 17:50:16.585 test[17527:4095662] 第0次 - <NSThread: 0x7ff5c948d310>{number = 2, name = (null)}

2017-04-11 17:50:16.586 test[17527:4095666] 第3次 - <NSThread: 0x7ff5c972caf0>{number = 4, name = (null)}

2017-04-11 17:50:16.586 test[17527:4095467] 第4次 - <NSThread: 0x7ff5c9701910>{number = 1, name = main}

NOTEaddExecutionBlock 方法必須在 start() 方法之前執(zhí)行,否則就會(huì)報(bào)錯(cuò):

‘*** -[NSBlockOperation addExecutionBlock:]: blocks cannot be added after the operation has started executing or finished'

  • 自定義Operation

    除了上面的兩種 Operation 以外柑蛇,我們還可以自定義 Operation芥挣。自定義 Operation 需要繼承 NSOperation類(lèi),并實(shí)現(xiàn)其 main() 方法耻台,因?yàn)樵谡{(diào)用 start()方法的時(shí)候空免,內(nèi)部會(huì)調(diào)用main() 方法完成相關(guān)邏輯。所以如果以上的兩個(gè)類(lèi)無(wú)法滿足你的欲望的時(shí)候盆耽,你就需要自定義了蹋砚。你想要實(shí)現(xiàn)什么功能都可以寫(xiě)在里面扼菠。除此之外,你還需要實(shí)現(xiàn) cancel() 在內(nèi)的各種方法坝咐。

創(chuàng)建隊(duì)列

看過(guò)上面的內(nèi)容就知道循榆,我們可以調(diào)用一個(gè) NSOperation 對(duì)象的 start() 方法來(lái)啟動(dòng)這個(gè)任務(wù),但是這樣做他們默認(rèn)是 同步執(zhí)行 的墨坚。就算是 addExecutionBlock 方法秧饮,也會(huì)在 當(dāng)前線程和其他線程 中執(zhí)行,也就是說(shuō)還是會(huì)占用當(dāng)前線程泽篮。這是就要用到隊(duì)列 NSOperationQueue 了盗尸。而且,按類(lèi)型來(lái)說(shuō)的話一共有兩種類(lèi)型:主隊(duì)列帽撑、其他隊(duì)列振劳。只要添加到隊(duì)列,會(huì)自動(dòng)調(diào)用任務(wù)的 start()方法

  • 主隊(duì)列
    細(xì)心的同學(xué)就會(huì)發(fā)現(xiàn)油狂,每套多線程方案都會(huì)有一個(gè)主線程(當(dāng)然啦历恐,說(shuō)的是iOS中,像 pthread 這種多系統(tǒng)的方案并沒(méi)有专筷,因?yàn)?UI線程 理論需要每種操作系統(tǒng)自己定制)弱贼。這是一個(gè)特殊的線程,必須串行磷蛹。所以添加到主隊(duì)列的任務(wù)都會(huì)一個(gè)接一個(gè)地排著隊(duì)在主線程處理吮旅。
//OBJECTIVE-C
NSOperationQueue *queue = [NSOperationQueue mainQueue];
//SWIFT
let queue = NSOperationQueue.mainQueue()
  • 其他隊(duì)列
    因?yàn)橹麝?duì)列比較特殊,所以會(huì)單獨(dú)有一個(gè)類(lèi)方法來(lái)獲得主隊(duì)列味咳。那么通過(guò)初始化產(chǎn)生的隊(duì)列就是其他隊(duì)列了庇勃,因?yàn)橹挥羞@兩種隊(duì)列,除了主隊(duì)列槽驶,其他隊(duì)列就不需要名字了责嚷。

    注意:其他隊(duì)列的任務(wù)會(huì)在其他線程并行執(zhí)行。

OBJECTIVE-C

//1.創(chuàng)建一個(gè)其他隊(duì)列    
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

//2.創(chuàng)建NSBlockOperation對(duì)象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"%@", [NSThread currentThread]);
}];

//3.添加多個(gè)Block
for (NSInteger i = 0; i < 5; i++) {
    [operation addExecutionBlock:^{
        NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
    }];
}

//4.隊(duì)列添加任務(wù)
[queue addOperation:operation];

SWIFT

//1.創(chuàng)建其他隊(duì)列
let queue = NSOperationQueue()

//2.創(chuàng)建NSBlockOperation對(duì)象
let operation = NSBlockOperation { () -> Void in
    NSLog("%@", NSThread.currentThread())
}

//3.添加多個(gè)Block
for i in 0..<5 {
    operation.addExecutionBlock { () -> Void in
        NSLog("第%ld次 - %@", i, NSThread.currentThread())
    }
}

//4.隊(duì)列添加任務(wù)
queue.addOperation(operation)

打印輸出

2017-04-11 20:26:28.463 test[18622:4443534] <NSThread: 0x7fd022c3ac10>{number = 5, name = (null)}
2017-04-11 20:26:28.463 test[18622:4443536] 第2次 - <NSThread: 0x7fd022e36d50>{number = 2, name = (null)}
2017-04-11 20:26:28.463 test[18622:4443535] 第0次 - <NSThread: 0x7fd022f237f0>{number = 4, name = (null)}
2017-04-11 20:26:28.463 test[18622:4443533] 第1次 - <NSThread: 0x7fd022d372b0>{number = 3, name = (null)}
2017-04-11 20:26:28.463 test[18622:4443534] 第3次 - <NSThread: 0x7fd022c3ac10>{number = 5, name = (null)}
2017-04-11 20:26:28.463 test[18622:4443536] 第4次 - <NSThread: 0x7fd022e36d50>{number = 2, name = (null)}

OK, 這時(shí)應(yīng)該發(fā)問(wèn)了掂铐,大家將 NSOperationQueueGCD的隊(duì)列 相比較就會(huì)發(fā)現(xiàn)罕拂,這里沒(méi)有串行隊(duì)列,那如果我想要10個(gè)任務(wù)在其他線程串行的執(zhí)行怎么辦全陨?

這就是蘋(píng)果封裝的妙處爆班,你不用管串行、并行辱姨、同步柿菩、異步這些名詞。NSOperationQueue 有一個(gè)參數(shù) maxConcurrentOperationCount 最大并發(fā)數(shù)雨涛,用來(lái)設(shè)置最多可以讓多少個(gè)任務(wù)同時(shí)執(zhí)行枢舶。當(dāng)你把它設(shè)置為 1的時(shí)候懦胞,他不就是串行了嘛!

NSOperationQueue 還有一個(gè)添加任務(wù)的方法祟辟,- (void)addOperationWithBlock:(void (^)(void))block;医瘫,這是不是和 GCD 差不多侣肄?這樣就可以添加一個(gè)任務(wù)到隊(duì)列中了旧困,十分方便。

NSOperation 有一個(gè)非常實(shí)用的功能稼锅,那就是添加依賴(lài)吼具。比如有 3 個(gè)任務(wù):A: 從服務(wù)器上下載一張圖片,B:給這張圖片加個(gè)水印矩距,C:把圖片返回給服務(wù)器拗盒。這時(shí)就可以用到依賴(lài)了:

OBJECTIVE-C

//1.任務(wù)一:下載圖片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"下載圖片 - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

//2.任務(wù)二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"打水印   - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

//3.任務(wù)三:上傳圖片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"上傳圖片 - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

//4.設(shè)置依賴(lài)
[operation2 addDependency:operation1];      //任務(wù)二依賴(lài)任務(wù)一
[operation3 addDependency:operation2];      //任務(wù)三依賴(lài)任務(wù)二

//5.創(chuàng)建隊(duì)列并加入任務(wù)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];

SWIFT

//1.任務(wù)一:下載圖片
let operation1 = NSBlockOperation { () -> Void in
    NSLog("下載圖片 - %@", NSThread.currentThread())
    NSThread.sleepForTimeInterval(1.0)
}

//2.任務(wù)二:打水印
let operation2 = NSBlockOperation { () -> Void in
    NSLog("打水印   - %@", NSThread.currentThread())
    NSThread.sleepForTimeInterval(1.0)
}

//3.任務(wù)三:上傳圖片
let operation3 = NSBlockOperation { () -> Void in
    NSLog("上傳圖片 - %@", NSThread.currentThread())
    NSThread.sleepForTimeInterval(1.0)
}

//4.設(shè)置依賴(lài)
operation2.addDependency(operation1)    //任務(wù)二依賴(lài)任務(wù)一
operation3.addDependency(operation2)    //任務(wù)三依賴(lài)任務(wù)二

//5.創(chuàng)建隊(duì)列并加入任務(wù)
let queue = NSOperationQueue()
queue.addOperations([operation3, operation2, operation1], waitUntilFinished: false)

打印結(jié)果

2017-04-11 21:24:28.622 test[19392:4637517] 下載圖片 - <NSThread: 0x7fc10ad4d970>{number = 2, name = (null)}
2017-04-11 21:24:29.622 test[19392:4637515] 打水印 - <NSThread: 0x7fc10af20ef0>{number = 3, name = (null)}
2017-04-11 21:24:30.627 test[19392:4637515] 上傳圖片 - <NSThread: 0x7fc10af20ef0>{number = 3, name = (null)}

注意:

  • 不能添加相互依賴(lài),會(huì)死鎖锥债,比如 A依賴(lài)B陡蝇,B依賴(lài)A。
  • 可以使用 removeDependency 來(lái)解除依賴(lài)關(guān)系哮肚。
  • 可以在不同的隊(duì)列之間依賴(lài)登夫,反正就是這個(gè)依賴(lài)是添加到任務(wù)身上的,和隊(duì)列沒(méi)關(guān)系允趟。

其他方法

以上就是一些主要方法, 下面還有一些常用方法需要大家注意:

NSOperation

    BOOL executing; //判斷任務(wù)是否正在執(zhí)行

    BOOL finished; //判斷任務(wù)是否完成

    void (^completionBlock)(void); //用來(lái)設(shè)置完成后需要執(zhí)行的操作

    - (void)cancel; //取消任務(wù)

    - (void)waitUntilFinished; //阻塞當(dāng)前線程直到此任務(wù)執(zhí)行完畢

NSOperationQueue

    NSUInteger operationCount; //獲取隊(duì)列的任務(wù)數(shù)

    - (void)cancelAllOperations; //取消隊(duì)列中所有的任務(wù)

    - (void)waitUntilAllOperationsAreFinished; //阻塞當(dāng)前線程直到此隊(duì)列中的所有任務(wù)執(zhí)行完畢

    [queue setSuspended:YES]; // 暫停queue

    [queue setSuspended:NO]; // 繼續(xù)queue
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末恼策,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子盆犁,更是在濱河造成了極大的恐慌蹭秋,老刑警劉巖侈沪,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異狮斗,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)弧蝇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)情龄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人捍壤,你說(shuō)我怎么就攤上這事骤视。” “怎么了鹃觉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵专酗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我盗扇,道長(zhǎng)祷肯,這世上最難降的妖魔是什么沉填? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮佑笋,結(jié)果婚禮上翼闹,老公的妹妹穿的比我還像新娘。我一直安慰自己蒋纬,他們只是感情好猎荠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著蜀备,像睡著了一般关摇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碾阁,一...
    開(kāi)封第一講書(shū)人閱讀 51,198評(píng)論 1 299
  • 那天输虱,我揣著相機(jī)與錄音,去河邊找鬼脂凶。 笑死宪睹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蚕钦。 我是一名探鬼主播亭病,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼冠桃!你這毒婦竟也來(lái)了命贴?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤食听,失蹤者是張志新(化名)和其女友劉穎胸蛛,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體樱报,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡葬项,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迹蛤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片民珍。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖盗飒,靈堂內(nèi)的尸體忽然破棺而出嚷量,到底是詐尸還是另有隱情,我是刑警寧澤逆趣,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布蝶溶,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏抖所。R本人自食惡果不足惜梨州,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望田轧。 院中可真熱鬧暴匠,春花似錦、人聲如沸傻粘。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)抹腿。三九已至岛请,卻和暖如春旭寿,著一層夾襖步出監(jiān)牢的瞬間警绩,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工盅称, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肩祥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓缩膝,卻偏偏與公主長(zhǎng)得像混狠,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疾层,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

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