iOS線程之NSOperation

前篇文章已經(jīng)講了GCD了够吩,那么這兩者有什么區(qū)別织鲸?

GCD VS NSOperation

"NSOperationQueue predates Grand Central Dispatch and on iOS it doesn't use GCD to execute operations (this is different on Mac OS X). It uses regular background threads which have a little more overhead than GCD dispatch queues.
On the other hand, NSOperationQueue gives you a lot more control over how your operations are executed. You can define dependencies between individual operations for example, which isn't possible with plain GCD queues. It is also possible to cancel operations that have been enqueued in an NSOperationQueue (as far as the operations support it). When you enqueue a block in a GCD dispatch queue, it will definitely be executed at some point.
To sum it up, NSOperationQueue can be more suitable for long-running operations that may need to be cancelled or have complex dependencies. GCD dispatch queues are better for short tasks that should have minimum performance and memory overhead."

簡單來說就是GCD偏底層點昔瞧,性能好角撞,依賴關(guān)系少梭姓,并發(fā)耗費資源少忧换。
NSOperation可觀察狀態(tài),性能也不錯蜻牢,處理事務(wù)更簡單操作烤咧。

對于這兩種都熟練運用的人來說,無所謂了抢呆,APP大多數(shù)事務(wù)這兩者都能完美解決煮嫌。至于代碼用哪個這個取決于你的興趣了。

下面詳細(xì)說一下NSOperation

@interface NSOperation : NSObject {

- (void)start; //開始執(zhí)行 默認(rèn)是同步執(zhí)行的
- (void)main; //主任務(wù)的函數(shù) 

@property (readonly, getter=isCancelled) BOOL cancelled; //是否取消
- (void)cancel; //取消任務(wù)

@property (readonly, getter=isExecuting) BOOL executing;//是否正在執(zhí)行
@property (readonly, getter=isFinished) BOOL finished; //是否完成
@property (readonly, getter=isConcurrent) BOOL concurrent; // To be deprecated; use and override 'asynchronous' below 是否并行
@property (readonly, getter=isAsynchronous) BOOL asynchronous NS_AVAILABLE(10_8, 7_0); //是否異步
@property (readonly, getter=isReady) BOOL ready; //是否正在等待

- (void)addDependency:(NSOperation *)op; //添加依賴
- (void)removeDependency:(NSOperation *)op; //刪除依賴關(guān)系

@property (readonly, copy) NSArray<NSOperation *> *dependencies; //所有依賴關(guān)系的數(shù)組

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
//隊列優(yōu)先級  優(yōu)先級高的先執(zhí)行 一般設(shè)置為0 即 NSOperationQueuePriorityNormal抱虐。
 NSOperationQueuePriorityVeryLow = -8L,
 NSOperationQueuePriorityLow = -4L,
 NSOperationQueuePriorityNormal = 0,
 NSOperationQueuePriorityHigh = 4,
 NSOperationQueuePriorityVeryHigh = 8
};

@property NSOperationQueuePriority queuePriority;//隊列優(yōu)先級

@property (nullable, copy) void (^completionBlock)(void)  NS_AVAILABLE(10_6, 4_0);//完成時候執(zhí)行的代碼塊
//等待直到完成
- (void)waitUntilFinished NS_AVAILABLE(10_6, 4_0);
//線程優(yōu)先級
@property double threadPriority NS_DEPRECATED(10_6, 10_10, 4_0, 8_0);

@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);
NSQualityOfService 的幾個枚舉值:
  NSQualityOfServiceUserInteractive:最高優(yōu)先級昌阿,主要用于提供交互UI的操作,比如處理點擊事件,繪制圖像到屏幕上
  NSQualityOfServiceUserInitiated:次高優(yōu)先級懦冰,主要用于執(zhí)行需要立即返回的任務(wù)
  NSQualityOfServiceDefault:默認(rèn)優(yōu)先級灶轰,當(dāng)沒有設(shè)置優(yōu)先級的時候,線程默認(rèn)優(yōu)先級
  NSQualityOfServiceUtility:普通優(yōu)先級刷钢,主要用于不需要立即返回的任務(wù)
  NSQualityOfServiceBackground:后臺優(yōu)先級笋颤,用于完全不緊急的任務(wù)


//名字
@property (nullable, copy) NSString *name NS_AVAILABLE(10_10, 8_0);

NSBlockOperation

- (void)print{
    NSLog(@"線程info : %@",[NSThread currentThread]);
}
- (void)test4{
    NSBlockOperation * blop = [[NSBlockOperation alloc]init];
    [blop addExecutionBlock:^{//添加同時執(zhí)行的task
        NSLog(@"1 start");
        [self print];
        sleep(2);
        
        NSLog(@"1 end");
    }];
    [blop addExecutionBlock:^{ //添加同時執(zhí)行的task
        NSLog(@"2 start");
        [self print];
        sleep(4);
        
        NSLog(@"2 end");
    }];
    [blop addExecutionBlock:^{ //添加同時執(zhí)行的task
        NSLog(@"3 start");
        [self print];
        sleep(1);
        
        NSLog(@"3 end");
    }];
    [blop setCompletionBlock:^{ //添加同時執(zhí)行的task
        NSLog(@"blop end");
    }];
    
    [blop start];
}
輸出:
**2016-03-29 16:47:44.857 GCD_Demo[17555:562249] 1 start**
**2016-03-29 16:47:44.857 GCD_Demo[17555:562287] 3 start**
**2016-03-29 16:47:44.857 GCD_Demo[17555:562288] 2 start**
**2016-03-29 16:47:44.857 GCD_Demo[17555:562249] ****線程****info : <NSThread: 0x7fea68408b30>{number = 1, name = main}**
**2016-03-29 16:47:44.857 GCD_Demo[17555:562288] ****線程****info : <NSThread: 0x7fea6861fb30>{number = 3, name = (null)}**
**2016-03-29 16:47:44.857 GCD_Demo[17555:562287] ****線程****info : <NSThread: 0x7fea69300470>{number = 2, name = (null)}**
**2016-03-29 16:47:45.922 GCD_Demo[17555:562287] 3 end**
**2016-03-29 16:47:46.858 GCD_Demo[17555:562249] 1 end**
**2016-03-29 16:47:48.928 GCD_Demo[17555:562288] 2 end**
**2016-03-29 16:47:48.929 GCD_Demo[17555:562288] blop end**

可以看出來,NSBlockOperation當(dāng)任務(wù)是1的時候在main線程中執(zhí)行内地,任務(wù)大于1的時候伴澄,其他的個自獨自開了線程,而且互不影響瓤鼻。

依賴關(guān)系

- (void)print{
    NSLog(@"線程info : %@",[NSThread currentThread]);
}
- (void)test4{
    NSBlockOperation * blop = [[NSBlockOperation alloc]init];
    [blop addExecutionBlock:^{
        NSLog(@"blop1_1 start");
        [self print];
        sleep(2);
        NSLog(@"blop1_1 end");
    }];
    [blop addExecutionBlock:^{
        NSLog(@"blop1_2 start");
        [self print];
        sleep(4);
        NSLog(@"blop1_2 end");
    }];
    NSLog(@"blop will start");
    [blop start];
    NSLog(@"blop did start");
    
    NSBlockOperation * blop2 =[NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blop2 start");
        [self print];
        sleep(2);
        NSLog(@"blop2 end");
    }];
   // [blop2 addDependency:blop];//blop2 依賴blop 就是blopExecutionBlock 執(zhí)行完之后再執(zhí)行blop2的任務(wù)【blop2 執(zhí)行task和blop 的CompletionBlock基本是同時執(zhí)行的】
    [blop2 start];
輸出:
**2016-03-29 17:06:53.217 GCD_Demo[17806:574416] blop will start**
**2016-03-29 17:06:53.217 GCD_Demo[17806:574416] blop1_1 start**
**2016-03-29 17:06:53.217 GCD_Demo[17806:574455] blop1_2 start**
**2016-03-29 17:06:53.217 GCD_Demo[17806:574416] ****線程****info : <NSThread: 0x7f839a004ff0>{number = 1, name = main}**
**2016-03-29 17:06:53.218 GCD_Demo[17806:574455] ****線程****info : <NSThread: 0x7f8398416d80>{number = 2, name = (null)}**
**2016-03-29 17:06:55.219 GCD_Demo[17806:574416] blop1_1 end**
**2016-03-29 17:06:57.272 GCD_Demo[17806:574455] blop1_2 end**
**2016-03-29 17:06:57.272 GCD_Demo[17806:574416] blop did start**
**2016-03-29 17:06:57.273 GCD_Demo[17806:574416] blop2 start**
**2016-03-29 17:06:57.273 GCD_Demo[17806:574416] ****線程****info : <NSThread: 0x7f839a004ff0>{number = 1, name = main}**
**2016-03-29 17:06:59.274 GCD_Demo[17806:574416] blop2 end**

從輸出的信息可以看出來秉版,block是同步執(zhí)行的,雖然多任務(wù)是多線程茬祷,但是主線程還是在阻塞中清焕,只有上一個所有 task 執(zhí)行完的時候,才會執(zhí)行下邊的task祭犯。所以在這里依賴關(guān)系不那么重要了秸妥,注釋掉運行結(jié)果也一樣的。

NSInvocationOperation

NSInvocationOperation 是NSOperation的子類沃粗,負(fù)責(zé)實現(xiàn)operation的SEL方法粥惧。
這樣子operation就可以start的時候執(zhí)行一些函數(shù)了。
在swift中已經(jīng)廢棄
看文檔:
NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available")

NSOperationQueue

//添加操作
- (void)addOperation:(NSOperation *)op;
//添加操作數(shù)組 在完成操作的時候
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);
//添加攜帶代碼塊的operation
- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);
//所有的操作 組成的數(shù)組 可讀屬性
@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;
//操作個數(shù)
@property (readonly) NSUInteger operationCount NS_AVAILABLE(10_6, 4_0);
//設(shè)置最大并行的任務(wù)數(shù) ps:operation 其實 一個operation可以同時開啟幾個線程的最盅。
@property NSInteger maxConcurrentOperationCount;
//掛起
@property (getter=isSuspended) BOOL suspended;
//隊列的名字
@property (nullable, copy) NSString *name NS_AVAILABLE(10_6, 4_0);
//優(yōu)先級
@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);
隊列
@property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue NS_AVAILABLE(10_10, 8_0);
//取消所有的操作
- (void)cancelAllOperations;
//等到他們的操作結(jié)束
- (void)waitUntilAllOperationsAreFinished;
//當(dāng)前的隊列
+ (nullable NSOperationQueue *)currentQueue NS_AVAILABLE(10_6, 4_0);
//主隊列
+ (NSOperationQueue *)mainQueue NS_AVAILABLE(10_6, 4_0);

 
# 隊列的例子
#隊列中添加的operation都是在子線程中執(zhí)行的突雪。
- (void)print{
    NSLog(@"線程info : %@",[NSThread currentThread]);
}
- (void)op1{
     NSLog(@"op1 開始運行了");
     sleep(3);
     NSLog(@"op1 結(jié)束");
}

- (void)test5{
    NSInvocationOperation * op1 =[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(op1) object:nil];
    NSOperationQueue * queue =[[NSOperationQueue alloc]init];
    [queue addOperation:op1]; //添加操作
    queue.maxConcurrentOperationCount = 1;//同時允許一個operation運行
    NSBlockOperation *block = [self test4];//任務(wù)塊
    [queue addOperation:block];//添加任務(wù)塊并運行

// sleep(2);  
   // [queue cancelAllOperations];
}
- (NSBlockOperation *)test4{
    NSBlockOperation * blop = [[NSBlockOperation alloc]init];
    [blop addExecutionBlock:^{
        NSLog(@"blop1_1 start");
        [self print];
        sleep(2);
        NSLog(@"blop1_1 end");
    }];
    [blop addExecutionBlock:^{
        NSLog(@"blop1_2 start");
        [self print];
        sleep(4);
        NSLog(@"blop1_2 end");
    }];
    return blop;
}
輸出:
**2016-03-31 11:22:16.663 GCD_Demo[26038:889212] op1 ****開始運行了**
**2016-03-31 11:22:19.737 GCD_Demo[26038:889212] op1 ****結(jié)束**
**2016-03-31 11:22:19.738 GCD_Demo[26038:889213] blop1_1 start**
**2016-03-31 11:22:19.738 GCD_Demo[26038:889226] blop1_2 start**
**2016-03-31 11:22:19.738 GCD_Demo[26038:889213] ****線程****info : <NSThread: 0x7fea3061d110>{number = 2, name = (null)}**
**2016-03-31 11:22:19.738 GCD_Demo[26038:889226] ****線程****info : <NSThread: 0x7fea31800140>{number = 3, name = (null)}**
**2016-03-31 11:22:21.808 GCD_Demo[26038:889213] blop1_1 end**
**2016-03-31 11:22:23.784 GCD_Demo[26038:889226] blop1_2 end**
# 從輸出的信息可以看出來,當(dāng)設(shè)置最大的operation為1的時候涡贱,相當(dāng)于這個隊列同步運行了咏删,不過這個同步的單位不是線程,而是operation问词。

當(dāng)把這兩句代碼加到 test5最后邊輸出結(jié)果是:
**2016-03-31 11:28:59.267 GCD_Demo[26113:892737] op1 ****開始運行了**
**2016-03-31 11:29:02.341 GCD_Demo[26113:892737] op1 ****結(jié)束**
從輸出結(jié)果得出:正在執(zhí)行的Operation無法stop督函,正在ready的operation直接跳過start,執(zhí)行complateBlock.狀態(tài)由ready改為canceld激挪。ps:注意看官方文檔
`Canceling the operations does not automatically remove them from the queue or stop those that are currently executing.`
正在執(zhí)行的不會從隊列中刪除也不會stop辰狡。
`For operations that are queued and waiting execution, the queue must still attempt to execute the operation before recognizing that it is canceled and moving it to the finished state. 
For operations that are already executing, the operation object itself must check for cancellation and stop what it is doing so that it can move to the finished state. 
In both cases, a finished (or canceled) operation is still given a chance to execute its completion block before it is removed from the queue.` 
正在隊列中等待的operation執(zhí)行的時候會檢測是否被cancenld,如果狀態(tài)是canceld垄分,那么直接執(zhí)行completion block 在它被隊列刪除的時候宛篇。

在子線程中耗時的操作完成了,那么該在主線程中更新UI

#將上面的test5 改成下面的代碼
- (void)test5{
    NSInvocationOperation * op1 =[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(op1) object:nil];
    NSOperationQueue * queue =[[NSOperationQueue alloc]init];
    [queue addOperation:op1];
    queue.maxConcurrentOperationCount = 3;//根據(jù)需要設(shè)置數(shù)量
    NSBlockOperation *block = [self test4];
    [queue addOperation:block];
//這句話一定要添加薄湿,這句話的意思等到所有的operation都完成了在執(zhí)行后面的代碼叫倍,
其實就是上面的操作執(zhí)行到這里要等待他們直到他們都完成了豌鸡。
#     [queue waitUntilAllOperationsAreFinished]; 
    NSBlockOperation * blockUpdateMainUI=[NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"update UI");
    }];
    [[NSOperationQueue mainQueue] addOperation:blockUpdateMainUI];//在主隊列中執(zhí)行更新UI的操作
}
上面的代碼 和GCD中的分組有些類似,但是 這個OperationQueue基本單位是operation而不是線程段标,一定要理解。
operation和線程的關(guān)系是 一個operation可能對應(yīng)多個線程炉奴,也可能對應(yīng)一個線程逼庞。

關(guān)于NSOperationQueue的了解和使用我想到的基本就這么多場景,后期有其他的場景再補充瞻赶。
預(yù)告:下期節(jié)目是NSThread的介紹和使用赛糟。
ps:廣告時間


更多文章:www.fgyong.cn
有問題可以發(fā)我郵箱討論共同交流技術(shù)。
fgyong@yeah.net

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末砸逊,一起剝皮案震驚了整個濱河市璧南,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌师逸,老刑警劉巖司倚,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異篓像,居然都是意外死亡动知,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門员辩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盒粮,“玉大人,你說我怎么就攤上這事奠滑〉ぶ澹” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵宋税,是天一觀的道長摊崭。 經(jīng)常有香客問我,道長弃甥,這世上最難降的妖魔是什么爽室? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮淆攻,結(jié)果婚禮上阔墩,老公的妹妹穿的比我還像新娘。我一直安慰自己瓶珊,他們只是感情好啸箫,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著伞芹,像睡著了一般忘苛。 火紅的嫁衣襯著肌膚如雪蝉娜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天扎唾,我揣著相機與錄音召川,去河邊找鬼。 笑死胸遇,一個胖子當(dāng)著我的面吹牛荧呐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纸镊,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼倍阐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了逗威?” 一聲冷哼從身側(cè)響起峰搪,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凯旭,沒想到半個月后概耻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡尽纽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年咐蚯,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弄贿。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡春锋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出差凹,到底是詐尸還是另有隱情期奔,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布危尿,位于F島的核電站呐萌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谊娇。R本人自食惡果不足惜肺孤,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望济欢。 院中可真熱鬧赠堵,春花似錦、人聲如沸法褥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽半等。三九已至揍愁,卻和暖如春呐萨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背莽囤。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工谬擦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人朽缎。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓怯屉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饵沧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

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

  • 現(xiàn)在iOS的多線程方案主要有以下這幾種: GCD(Grand Central Dispatch):使用dispat...
    寒光冷劍閱讀 1,546評論 0 2
  • 一是钥、前言 上一篇文章iOS多線程淺匯-原理篇中整理了一些有關(guān)多線程的基本概念。本篇博文介紹的是iOS中常用的幾個多...
    nuclear閱讀 2,046評論 6 18
  • iOS Concurrency Programming Guide iOS 和 Mac OS 傳統(tǒng)的并發(fā)編程模型是...
    YangPu閱讀 833評論 0 2
  • 簡介 在iOS中缅叠,我們需要將非UI且耗時的任務(wù)放在主線程當(dāng)中執(zhí)行悄泥,同時確保在任務(wù)完成時進行回調(diào)。常用的三種實現(xiàn)多線...
    adduct閱讀 373評論 0 1
  • Object C中創(chuàng)建線程的方法是什么肤粱?如果在主線程中執(zhí)行代碼弹囚,方法是什么?如果想延時執(zhí)行代碼领曼、方法又是什么鸥鹉? 1...
    AlanGe閱讀 1,716評論 0 17