前篇文章已經(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