iOS多線程-NSOperation

NSOperation是對GCD的高級封裝。相對于GCD,使用NSOperation更加符合面對對象的編程習(xí)慣,更重要的是,NSOperation提供了更多的特性方便開發(fā)者監(jiān)控和管理要并發(fā)執(zhí)行的任務(wù):

  1. 可以簡便地設(shè)置任務(wù)之間的依賴關(guān)系
  2. 可以對取消正在執(zhí)行的任務(wù)
  3. 可以只用KVO的方式來監(jiān)控任務(wù)的當(dāng)前狀態(tài)
  4. 可以針對任務(wù)設(shè)置優(yōu)先級
  5. 可以為任務(wù)設(shè)置一個(gè)完成后執(zhí)行的competitionBlock

創(chuàng)建NSOperation

NSOperation有兩個(gè)具體的子類庭呜,NSInvocationOperation和NSBlockOperation,創(chuàng)建方法如下(官方Demo):

@implementation MyCustomClass
- (NSOperation*)taskWithData:(id)data {
    NSInvocationOperation* theOp = [[NSInvocationOperation alloc] initWithTarget:self
                    selector:@selector(myTaskMethod:) object:data];
 
   return theOp;
}
 
// This is the method that does the actual work of the task.
- (void)myTaskMethod:(id)data {
    // Perform the task.
}
@end
NSBlockOperation* theOp = [NSBlockOperation blockOperationWithBlock: ^{
      NSLog(@"Beginning operation.\n");
      // Do some work.
   }];

NSOperation的同步和異步執(zhí)行

NSOperation對象調(diào)用start方法執(zhí)行犀忱,默認(rèn)是在調(diào)用的線程同步執(zhí)行的募谎。當(dāng)一個(gè)線程調(diào)用start方法時(shí),operation會立即在該線程中執(zhí)行阴汇。當(dāng)operation結(jié)束后線程才會繼續(xù)執(zhí)行之后的代碼数冬。

對一個(gè)異步的operation來說,當(dāng)調(diào)用其start方法時(shí)搀庶,該operation可能會開啟一個(gè)新的線程執(zhí)行拐纱,調(diào)用一個(gè)異步方法或者將block提交到一個(gè)dispatch_queue,總之調(diào)用的線程會繼續(xù)執(zhí)行余下的代碼而不會等待operation完成哥倔。

NSOperationQueue

大多數(shù)情況下并不需要?jiǎng)?chuàng)建一個(gè)異步的operation秸架,這需要開發(fā)者做很多額外的工作。更普遍的做法是將NSOperation放進(jìn)NSOperationQueue中去執(zhí)行咆蒿。當(dāng)你將一個(gè)operation加進(jìn)operation queue中东抹,那么無論該operation的asynchronous 屬性是什么,調(diào)用者都會在一個(gè)另外一個(gè)的線程中去執(zhí)行start方法沃测。所以當(dāng)使用NSOperationQueue來管理執(zhí)行的話缭黔,完全沒有必要?jiǎng)?chuàng)建異步的operation。

當(dāng)operation添加進(jìn)operation queue后蒂破,不需要調(diào)用任何方法馏谨,operation queue 中的operation就會開始執(zhí)行。

設(shè)置依賴

用法簡單附迷,直接上一段代碼:

- (void)opDependency{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSOperation *calculateOp1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start do some calulation 1");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"end cal 1, do some update");
    }];
    [queue addOperation:calculateOp1];
    
    NSOperation *calculateOp2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start do some calulation 2");
        [NSThread sleepForTimeInterval:2];
        NSLog(@"end cal 2, do some update");
    }];
    [queue addOperation:calculateOp2];
    
    NSOperation *doAfterPreOpDone = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"do something which depend on op1 and op2");
    }];
    [doAfterPreOpDone addDependency:calculateOp1];
    [doAfterPreOpDone addDependency:calculateOp2];
    
    [queue addOperation:doAfterPreOpDone];
}

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

2017-04-20 15:53:06.062 ConcurrentProgram[98523:43310892] start do some calulation 1
2017-04-20 15:53:06.062 ConcurrentProgram[98523:43310890] start do some calulation 2
2017-04-20 15:53:08.134 ConcurrentProgram[98523:43310890] end cal 2, do some update
2017-04-20 15:53:09.137 ConcurrentProgram[98523:43310892] end cal 1, do some update
2017-04-20 15:53:09.137 ConcurrentProgram[98523:43310890] do something which depend on op1 and op2

取消NSOperation

將上面的代碼稍微改動一下惧互,測試一下NSOperation的cancel功能。

- (void)opDependency{
    __block long i = 0;
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSOperation *calculateOp1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start do some calulation 1");
        while (true) {
            i++;
        }
    }];
    [queue addOperation:calculateOp1];
    
    NSOperation *calculateOp2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start do some calulation 2");
        [NSThread sleepForTimeInterval:2];
        NSLog(@"end cal 2, do some update");
        [calculateOp1 cancel];
    }];
    [queue addOperation:calculateOp2];
    
    NSOperation *doAfterPreOpDone = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"do something which depend on op1 and op2");
        NSLog(@"i is %ld",i);
    }];
    [doAfterPreOpDone addDependency:calculateOp1];
    [doAfterPreOpDone addDependency:calculateOp2];
    
    [queue addOperation:doAfterPreOpDone];
}

在calculateOp1中設(shè)置了一個(gè)無限循環(huán)的block挟秤,然后我們希望在calculateOp2中去取消掉calculateOp1。預(yù)期結(jié)果是在calculateOp2中的block執(zhí)行完以后會立即執(zhí)行doAfterPreOpDone中的任務(wù)抄伍。(因?yàn)榱硪粋€(gè)依賴被取消了)

結(jié)果如下:

2017-04-20 16:38:01.658 ConcurrentProgram[542:43544549] start do some calulation 1
2017-04-20 16:38:01.658 ConcurrentProgram[542:43544548] start do some calulation 2
2017-04-20 16:38:03.730 ConcurrentProgram[542:43544548] end cal 2, do some update

發(fā)現(xiàn)calculateOp1并沒有取消而doAfterPreOpDone一直不會執(zhí)行艘刚。

去查閱官方的API文檔,發(fā)現(xiàn)有這樣的一段話:

Canceling an operation does not immediately force it to stop what it is doing. Although respecting the value in the cancelled property is expected of all operations, your code must explicitly check the value in this property and abort as needed. The default implementation of NSOperation includes checks for cancellation. For example, if you cancel an operation before its start method is called, the start method exits without starting the task.

也就是說調(diào)用cancel方法并不會立即停止operation中的動作截珍,只是將cancelled屬性設(shè)置為YES,真正的取消動作是需要開發(fā)者自己實(shí)現(xiàn)的攀甚。一般來說箩朴,要繼承NSOperation類,在start方法中不停地檢查isCancel屬性秋度。在默認(rèn)的NSOeration的實(shí)現(xiàn)中也包括了cancel屬性的檢查炸庞,如果你在operation的start方法之前cancel了,那么該operation就不會執(zhí)行了荚斯。

官方提供了一個(gè)示例(將main方法改為start方法效果一樣)

- (void)main {
   @try {
      BOOL isDone = NO;
 
      while (![self isCancelled] && !isDone) {
          // Do some work and set isDone to YES when finished
      }
   }
   @catch(...) {
      // Do not rethrow exceptions.
   }
}   

那么之前的代碼相當(dāng)于:

- (void)main {
    @try {
        BOOL isDone = NO;
        
        while (![self isCancelled] && !isDone) {
            NSLog(@"start do some calulation 1");
            while (true) {
                i++;
            }
        }
    }
    @catch(...) {
        // Do not rethrow exceptions.
    }
}

檢查cancel屬性的代碼在打印之前埠居,在打印之后取消該block就沒有效果了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末事期,一起剝皮案震驚了整個(gè)濱河市滥壕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌兽泣,老刑警劉巖绎橘,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異唠倦,居然都是意外死亡称鳞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門稠鼻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冈止,“玉大人,你說我怎么就攤上這事枷餐“腥常” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵毛肋,是天一觀的道長怨咪。 經(jīng)常有香客問我,道長润匙,這世上最難降的妖魔是什么诗眨? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮孕讳,結(jié)果婚禮上匠楚,老公的妹妹穿的比我還像新娘。我一直安慰自己厂财,他們只是感情好芋簿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著璃饱,像睡著了一般与斤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天撩穿,我揣著相機(jī)與錄音磷支,去河邊找鬼。 笑死食寡,一個(gè)胖子當(dāng)著我的面吹牛雾狈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播抵皱,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼善榛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了叨叙?” 一聲冷哼從身側(cè)響起锭弊,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎擂错,沒想到半個(gè)月后味滞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钮呀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年剑鞍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爽醋。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚁署,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蚂四,到底是詐尸還是另有隱情光戈,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布遂赠,位于F島的核電站久妆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏跷睦。R本人自食惡果不足惜筷弦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望抑诸。 院中可真熱鬧烂琴,春花似錦、人聲如沸蜕乡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽层玲。三九已至号醉,卻和暖如春绒瘦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背扣癣。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留憨降,地道東北人父虑。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像授药,于是被迫代替她去往敵國和親士嚎。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

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