iOS多線程之NSOperation

NSOperation是Apple基于GCD的封裝,面向?qū)ο螅橄髮哟胃撸褂煤唵巍N覀冎恍韪鶕?jù)任務(wù)類型創(chuàng)建合適的NSOperation,配置好相應(yīng)的參數(shù),丟到NSOperationQueue中,剩下的工作就交給系統(tǒng)調(diào)度處理,我們無需關(guān)心。是不是很誘人?

本篇文章主要內(nèi)容:

  • NSOperation和NSOperationQueue簡介
  • **創(chuàng)建操作對象的三種方式 **
  • 設(shè)置操作的優(yōu)先級
  • 設(shè)置操作的依賴
  • 獲取操作的狀態(tài)
  • 取消操作
  • 暫停和恢復(fù)操作隊列
  • 操作完成的回調(diào)

NSOperation和NSOperationQueue簡介

NSOperation和NSOperationQueue理解起來很容易,我們可以參照GCD相關(guān)內(nèi)容:NSOperation相當(dāng)于GCD中的任務(wù)塊,而NSOperationQueue相當(dāng)于GCD中的并發(fā)隊列。使用NSOperation編程用到的類主要有NSOperationNSBlockOperationNSInvocationOperationNSOperationQueue。NSBlockOperation和NSInvocationOperation是NSOperation的子類,我們不會使用NSOperation這個抽象基類,而是使用NSBlockOperation和NSInvocationOperation創(chuàng)建我們的任務(wù)。下面的內(nèi)容會具體介紹這些類的使用。代碼地址

創(chuàng)建操作對象的三種方式

1.NSInvocationOperation

初始化方法:

initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;

代碼示例:

NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(sayHello) object:nil];
- (void)sayHello
{
    NSLog(@"Hello World");
}

如果我們創(chuàng)建了一個NSInvocationOperation對象,不放入隊列中執(zhí)行,而是直接調(diào)用start方法會是怎樣的呢?

NSInvocationOperation *downloadOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadPic) object:nil];
    [downloadOperation start];
    NSLog(@"complete");
- (void)downloadPic
{
    NSLog(@"start download");
    [NSThread sleepForTimeInterval:2.0];
}

運行后發(fā)現(xiàn):打印 "start download" 后,過了2秒才打印 "complete",可以看出沒有放入隊列而直接調(diào)用start運行的操作,會阻塞當(dāng)前線程,是同步的,只有添加到隊列后,操作才會異步執(zhí)行。下面的NSBlockOperation同理。

2.NSBlockOperation

初始化方法:

blockOperationWithBlock:(void (^)(void))block;

添加操作:

addExecutionBlock:(void (^)(void))block;

代碼示例:

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start download ------ %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0];
    }];
    
    [blockOperation addExecutionBlock:^{
        NSLog(@"hello1 ------ %@", [NSThread currentThread]);
    }];
    
    [blockOperation addExecutionBlock:^{
        NSLog(@"hello2 ------ %@", [NSThread currentThread]);
    }];
    
    [blockOperation addExecutionBlock:^{
        NSLog(@"hello3 ------ %@", [NSThread currentThread]);
    }];
    
    [blockOperation start];
    
    NSLog(@"complete------ %@", [NSThread currentThread]);

從運行結(jié)果可以看出,通過blockOperationWithBlock創(chuàng)建的操作永遠(yuǎn)在主線程執(zhí)行,addExecutionBlock添加的其他操作會分發(fā)到子線程執(zhí)行。

3.繼承自NSOperation

有些情況下,前面兩種方式不能滿足我們的需求,就需要自定義NSOperation了。我們可以創(chuàng)建兩種類型的NSOperation:非并發(fā)的和并發(fā)的。本人能力有限,恐介紹不全,想要徹底吃透這塊需要一定時間,建議大家直接參照Apple提供的官方文檔及示例代碼

設(shè)置操作的優(yōu)先級

有時候我們想提前或推遲一些操作的執(zhí)行,就可以通過設(shè)置操作的優(yōu)先級來達到目的。操作的優(yōu)先級分為以下幾個級別:

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

我們需要注意的是:操作的優(yōu)先級是相對于同一個操作隊列中的其他操作而言的,不同操作隊列中的操作的優(yōu)先級沒有可比性。例如:操作隊列1中的操作A的優(yōu)先級為NSOperationQueuePriorityVeryHigh,操作隊列2中的操作B的優(yōu)先級為NSOperationQueuePriorityVeryLow,至于A和B誰先執(zhí)行,完全不確定。

代碼示例:

NSBlockOperation *downloadPicOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start download picture ------ %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0];//模擬下載操作
    }];
    [downloadPicOperation setQueuePriority:NSOperationQueuePriorityVeryLow];
    
    
    NSBlockOperation *downloadMusicOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start download music ------ %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:4.0];//模擬下載操作
    }];
    [downloadMusicOperation setQueuePriority:NSOperationQueuePriorityVeryHigh];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:downloadPicOperation];
    [queue addOperation:downloadMusicOperation];

多次運行代碼,我們發(fā)現(xiàn)優(yōu)先級高的不一定會早于優(yōu)先級低的任務(wù)執(zhí)行,這就是我們需要注意的另外一點:優(yōu)先級高的操作先執(zhí)行的概率大,但并不表示必然先執(zhí)行。設(shè)置優(yōu)先級的代碼要在操作放入操作隊列之前,否則是不起作用的或者說會產(chǎn)生不利的影響。

設(shè)置操作的依賴

在并發(fā)編程中,如果任務(wù)之間沒有執(zhí)行的先后關(guān)系,那么它們并發(fā)執(zhí)行是沒問題的,但還有一種情況是某個任務(wù)的執(zhí)行需要其他任務(wù)的執(zhí)行結(jié)果,這時候就要通過設(shè)置操作的依賴來達到目的,類似于GCD的同步。

代碼示例:

- (void)startAction
{
    NSBlockOperation *downloadPicOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start download picture ------ %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0];//模擬下載操作
    }];
    
    NSBlockOperation *prepareOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"prepare download picture ------ %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];//模擬下載前的準(zhǔn)備工作
    }];
    
    [downloadPicOperation addDependency:prepareOperation];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:downloadPicOperation];
    [queue addOperation:prepareOperation];
}

如代碼所示,我們對下載圖片操作添加了依賴,無論運行多少次,下載操作都是等到準(zhǔn)備操作執(zhí)行完成后才執(zhí)行的。當(dāng)然,對操作添加依賴也可以發(fā)生在不同隊列中,需要注意的是不能形成循環(huán)依賴關(guān)系,會導(dǎo)致死鎖。設(shè)置依賴的代碼要在操作放入操作隊列之前,否則是不起作用的或者說會產(chǎn)生不利的影響。與添加依賴對應(yīng)的操作是移除依賴:

- (void)removeDependency:(NSOperation *)op;

獲取操作的狀態(tài)

操作的生命周期可以表示為:ready —> excute —> finish ,我們將操作放入操作隊列后,操作的isReady狀態(tài)取決于其依賴是否都已執(zhí)行完成,操作的isExecuting狀態(tài)表示操作正在執(zhí)行,操作的isFinished狀態(tài)表示操作已經(jīng)執(zhí)行完成或者被cancel掉了。操作同一時刻只可能是以上三種狀態(tài)中的一種。

取消操作

取消單個操作,我們可以調(diào)用cancel方法;取消操作隊列中的所有操作,我們可以調(diào)用cancelAllOperations方法。我們最好是在確定不需要某個操作的時候才取消它,因為一旦取消,這個操作就被作為finished處理。

暫停和恢復(fù)操作隊列

暫停操作隊列:

[queue setSuspended:YES];

恢復(fù)操作隊列:

[queue setSuspended:NO];

當(dāng)我們暫停某個操作隊列時,操作隊列就會停止調(diào)度新的操作執(zhí)行,而正在執(zhí)行的操作不會被停止。

操作完成的回調(diào)

代碼示例:

- (void)startAction
{
    NSBlockOperation *downloadPicOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start download picture ------ %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2.0];//模擬下載操作
    }];
    
    [downloadPicOperation setCompletionBlock:^{
        NSLog(@"download picture complete");
    }];
    
    NSBlockOperation *downloadMusicOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"start download music ------ %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:4.0];//模擬下載操作
    }];
    
    [downloadMusicOperation setCompletionBlock:^{
         NSLog(@"download music complete");
    }];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:downloadPicOperation];
    [queue addOperation:downloadMusicOperation];
}

操作完成的回調(diào)可以用于通知主線程任務(wù)完成,注意:如果在回調(diào)中更新UI,需要派發(fā)到主線程執(zhí)行。

至此,iOS多線程之NSOperation相關(guān)內(nèi)容就總結(jié)完了,加上之前的兩篇:iOS多線程之GCD、iOS多線程之pthread和NSThread,iOS并發(fā)編程基本就覆蓋全面了。學(xué)以致用,希望自己在以后的實踐中不斷地總結(jié)提升。

最后編輯于
?著作權(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)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(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
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,978評論 2 374

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