iOS多線程之NSOperation

前言

GCD是一種很強(qiáng)大的多線程解決方案,但NSOperation同樣也支持多樣性的操作。

NSOperation有三種狀態(tài)

isReady -> isExecution -> isFinish

  • isReady: 返回 YES 表示操作已經(jīng)準(zhǔn)備好被執(zhí)行, 如果返回NO則說明還有其他沒有先前的相關(guān)步驟沒有完成。
  • isExecuting: 返回YES表示操作正在執(zhí)行,反之則沒在執(zhí)行。
  • isFinished : 返回YES表示操作執(zhí)行成功或者被取消了

NSOperationQueue只有當(dāng)它管理的所有操作的isFinished屬性全標(biāo)為YES以后操作才停止出列,也就是隊列停止運行,所以正確實現(xiàn)這個方法對于避免死鎖很關(guān)鍵。

簡單使用NSOperation

NSOperation不可以直接創(chuàng)建,但是我們可以使用它的子類NSBlockOperationNSInvocationOperation,前者是使用Block的方式,使用起來比較方便。

NSOperationQueue使用

類似Java線程池,可以先創(chuàng)建一個線程隊列

NSOperationQueue *queue = [[NSOperationQueue alloc]init];
queue.maxConcurrentOperationCount = 2; //最大并發(fā)數(shù)

或者獲取main隊列

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperation:operation];

NSBlockOperation 使用

再創(chuàng)建NSInvocationOperation或者NSBlockOperation的字例,添加到NSOperationQueue當(dāng)中,隊列就會依次執(zhí)行線程

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{

UIImage *image = [weakSelf doLoadImageWithURLString:@"http:xxx.jpg"];
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
    weakSelf.imageView2.image = image;
    }];
}];
[queue addOperation:operation2];
    [weakSelf nsBlockOperationLoadImage];
}];

也可以直接

[_operationQueue addOperationWithBlock:^{

}];

有時候使直接調(diào)用start方法,但是這樣子就是使當(dāng)前的線程阻塞。所以我不是不建議這樣子做滴。

NSInvocationOperation

創(chuàng)建NSInvocationOperation

    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(nsinvocationOperationLoadImage) object:nil]`      

下面這個是加載圖片的方法

    -(void)nsinvocationOperationLoadImage{

        __weak NSOperationViewController *weakSelf = self;

        UIImage *image = [self doLoadImageWithURLString:@"http://e.hiphotos.baidu.com/image/pic/item/c8ea15ce36d3d539228bfe2f3887e950342ab0ac.jpg"];
        [[NSOperationQueue mainQueue]addOperationWithBlock:^{
                [weakSelf.NSInvocationOperationImageView setImage:image];
        }];
    }

從上面看來NSOperation是不是比GCD方便很多呢?

NSOperation進(jìn)階

優(yōu)先級

跟NSThread一樣,NSOpertion也可以設(shè)置優(yōu)先級。

@property NSOperationQueuePriority queuePriority;

執(zhí)行順序(依賴)

有些時候想要控制執(zhí)行順序,使用NSOpreation會方便多了,使用NSOpreation的Dependency就可以實現(xiàn)這種功能。

  NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"excute operation2");        
  }];
  NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"excute operation1");
  }];
  [ope0ration2 addDependency:ope0ration1];
  [queue addOperation:ope0ration1]; 
  [queue addOperation:ope0ration2];

上面先執(zhí)行第一個operation1,等operation1返回isFinish為YES,即operation1完成了,才會執(zhí)行operation2。

注意死鎖:一定不可以循環(huán)依賴,像A依賴B,B依賴A,一定不要這樣做

CompletionBlock

這個比較容易理解,就是每個NSOperation執(zhí)行完畢之后,就會執(zhí)行該block

NSOperationQueue *queue = [NSOperationQueue mainQueue];
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"執(zhí)行操作");
}];
[operation setCompletionBlock:^{
    NSLog(@"執(zhí)行操作完成");
}];
[queue addOperation:operation];

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

2015-09-22 23:47:47.640 Thread Learn[21307:662442] 執(zhí)行操作
2015-09-22 23:47:47.640 Thread Learn[21307:662482] 執(zhí)行操作完成

取消

如前面所說,NSOperation有三種狀態(tài),isReady -> isExecuting -> isFinish, 如果在Ready的狀態(tài)中對NSOperation進(jìn)行取消,NSOperation會進(jìn)入Finish狀態(tài)。但是Operation已經(jīng)開始執(zhí)行了,就會一直運行到結(jié)束,或者由我們進(jìn)行顯示取消。也就是說Operation已經(jīng)在executing狀態(tài),我們調(diào)用cancle方法系統(tǒng)不會中止線程的,這需要我們在任務(wù)過程中檢測取消事件,并中止線程的執(zhí)行,還要注意一點我們要釋放內(nèi)存或資源。還是看一下實例代碼:

- (IBAction)startNSOperation:(id)sender {

    self.blockOperation = [NSBlockOperation blockOperationWithBlock:^{

    if ([self.blockOperation isCancelled]) {
        NSLog(@"取消了");
        return;
    }
    //如果檢測還沒取消
    //TODO:這里請求網(wǎng)絡(luò),獲取數(shù)據(jù)..


    if ([self.blockOperation isCancelled]) {
        NSLog(@"取消了");
        return;
    }   
    //如果檢測還沒取消
    //TODO:獲取到了數(shù)據(jù)刷新界面...
    }];

    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    [queue addOperation:self.blockOperation];
}

這種取消跟NSThread有點相似,調(diào)用cancle不會退出線程,需要你自已去中止線程,再exit;

自定義NSOperation

如果NSBlockOperation和NSInvocationOperation都不能滿足你應(yīng)用的需求,你可以選擇繼承NSOperation并做你想做的操作。

自定義非并發(fā)

繼承非并發(fā)的Operation比并發(fā)的要容易的多,只需要實現(xiàn)以下兩個方法就行了

  • 自定義初始化方法
  • main方法

需要自定義初始化方法改變Operation的狀態(tài),而把你的實現(xiàn)代碼放到main方法里。先看一個簡單的例子:

@interface MyNonConcurrentOperation : NSOperation
@property id (strong) myData;
-(id)initWithData:(id)data;
@end

@implementation MyNonConcurrentOperation
- (id)initWithData:(id)data {
   if (self = [super init]){
        myData = data;
    }
   return self;
}
-(void)main {
   @try {
      // Do some work on myData and report the results.
   }
   @catch(...) {
      // Do not rethrow exceptions.
   }
}
@

很簡單,上面的代碼提供了一個參數(shù)為data的初始化方法,而你可以在main里面寫上你的代碼。

你還可以從這里下載這份代碼

并發(fā)

自定義并發(fā)的NSOperation就麻煩多了,我們可以看一下下面這個表(來自蘋果官方):

方法 描述
start (必選)所有的并發(fā)Operation必需重寫這個方法并且要實現(xiàn)這個方法的內(nèi)容來代替原來的操和。手動執(zhí)行一個操作,你可以調(diào)用start方法。因此,這個方法的實現(xiàn)是這個操作的始點,也是其他線程或者運行這你這個任務(wù)的起點。注意一下,在這里永遠(yuǎn)不要調(diào)用[super start]。
main (可選)這個方法就是你的實現(xiàn)的操作(懶得翻譯了,哈哈)
isExecuting 和 isFinish (必選)并發(fā)隊列負(fù)責(zé)維持當(dāng)前操作的環(huán)境和告訴外部調(diào)用者當(dāng)前的運行狀態(tài)。因此,一個并發(fā)隊列必需維持保持一些狀態(tài)信息以至于知道什么時候執(zhí)行任務(wù),什么時候完成任務(wù)。它必須通過這些方法告訴外部當(dāng)前的狀態(tài)。這種而且這些方法必須是線程安全,當(dāng)狀態(tài)發(fā)生改變的時候,你必須使用KVO通知監(jiān)聽這些狀態(tài)的對象。
isConcurrent (必選)定義一個并發(fā)操作,重寫這個方法并且返回YES

話不多說,選看一下例子:

@interface MyOperation : NSOperation {
    BOOL        executing;
    BOOL        finished;
}
- (void)completeOperation;
@end
 
@implementation MyOperation
- (id)init {
    self = [super init];
    if (self) {
        executing = NO;
        finished = NO;
    }
    return self;
}
 
- (BOOL)isConcurrent {
    return YES;
}
 
- (BOOL)isExecuting {
    return executing;
}
 
- (BOOL)isFinished {
    return finished;
}
@end

上面的代碼簡單實現(xiàn)了isFinishisExecutingisConcurrent三個方法,isConcurrent只需要返回YES就可以了。isFinishisExecuting返回當(dāng)前實例的屬性就可以了。

- (void)start {
    [self.lock lock];
   // 在開始任務(wù)之前要測試一下是否取消
   if ([self isCancelled])
   {
      // 如果是已經(jīng)取消了,必需要把Finish設(shè)為YES
      [self willChangeValueForKey:@"isFinished"];
      finished = YES;
      [self didChangeValueForKey:@"isFinished"];
      return;
   }
 
   // 如果沒有取消,就繼續(xù)運行代碼
   [self willChangeValueForKey:@"isExecuting"];
   [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
   executing = YES;
   [self didChangeValueForKey:@"isExecuting"];
   [self.lock unlock];
}

- (void)main {
   @try {
 
       // 寫你業(yè)務(wù)代碼
 
       [self completeOperation];
   }
   @catch(...) {
      // Do not rethrow exceptions.
   }
}
 
- (void)completeOperation {
     [self.lock lock];
    [self willChangeValueForKey:@"isFinished"];
    [self willChangeValueForKey:@"isExecuting"];
 
    executing = NO;
    finished = YES;
 
    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
     [self.lock unlock];
}

注意你的實現(xiàn)要發(fā)出合適的KVO通知,因為如果你的NSOperation實現(xiàn)需要用到工作依賴從屬特性,而你的實現(xiàn)里沒有發(fā)出合適的“isFinished”KVO通知,依賴你的NSOperation就無法正常執(zhí)行。NSOperation支持KVO的屬性有:

  • isCancelled
  • isConcurrent
  • isExecuting
  • isFinished
  • isReady
  • dependencies
  • queuePriority
  • completionBlock

當(dāng)然也不是說所有的KVO通知都需要自己去實現(xiàn),例如通常你用不到addObserver到你工作的“isCancelled”屬性,你只需要直接調(diào)用cancel方法就可以取消這個工作任務(wù)。

參考文章

NShipster NSOperation

Concurrency Programming Guide

iOS多線程編程Part 2/3 - NSOperation

NSOperationQueue and NSOperation

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,978評論 2 374

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