緊接著上一篇GCD 之后 今天給大家 分享和總結(jié)的是NSOperation
廢話(huà)不多說(shuō):來(lái)看看NSOperation 是什么鬼?
官網(wǎng)的解釋是:

翻譯:一個(gè)抽象類(lèi),表示與單個(gè)任務(wù)關(guān)聯(lián)的代碼和數(shù)據(jù)
如果你有閱讀過(guò) SDWebImage和AFNetworking的源碼的話(huà) 那么你就會(huì)注意到里面大量的用到了大量的 NSOperation 為什么呢?
原因:
NSOperation、NSOperationQueue 是基于 GCD 更高一層的封裝,完全面向?qū)ο?。但是?GCD 更簡(jiǎn)單易用、代碼可讀性也更高。
- 可添加完成的代碼塊,在操作完成后執(zhí)行。
- 添加操作之間的依賴(lài)關(guān)系,方便的控制執(zhí)行順序。
- 設(shè)定操作執(zhí)行的優(yōu)先級(jí)。
- 可以很方便的取消一個(gè)操作的執(zhí)行。
- 使用 KVO 觀(guān)察對(duì)操作執(zhí)行狀態(tài)的更改:
isExecuteing、isFinished、isCancelled。
NSOperation的使用
NS_CLASS_AVAILABLE(10_6, 4_0)
@interface NSBlockOperation : NSOperation {
@private
id _private2;
void *_reserved2;
}
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
- (void)addExecutionBlock:(void (^)(void))block;
@property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;
@end
NS_CLASS_AVAILABLE(10_5, 2_0)
NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available")
@interface NSInvocationOperation : NSOperation {
@private
id _inv;
id _exception;
void *_reserved2;
}
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
- (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;
@property (readonly, retain) NSInvocation *invocation;
@property (nullable, readonly, retain) id result;
@end
NSOperation不可以直接創(chuàng)建,但是可以使用它的子類(lèi)NSBlockOperation和NSInvocationOperation,前者是使用Block的方式,使用起來(lái)比較方便。
NSBlockOperation
/**
* 使用子類(lèi) NSBlockOperation
*/
- (void)useBlockOperation {
// 1.創(chuàng)建 NSBlockOperation 對(duì)象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
// 2.調(diào)用 start 方法開(kāi)始執(zhí)行操作
[op start];
}

可以看出NSBlockOperation 默認(rèn)在主線(xiàn)程中執(zhí)行

看下addExecutionBlock
/**
* 使用子類(lèi) NSBlockOperation
* 調(diào)用方法 AddExecutionBlock:
*/
- (void)useBlockOperationAddExecutionBlock {
// 1.創(chuàng)建 NSBlockOperation 對(duì)象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
// 2.添加額外的操作
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"4---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"5---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"6---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"7---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"8---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
// 3.調(diào)用 start 方法開(kāi)始執(zhí)行操作
[op start];
}

// 1.創(chuàng)建 NSBlockOperation 對(duì)象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
可以看出上面是在主線(xiàn)程中執(zhí)行
使用自定義繼承自 NSOperation 的子類(lèi)
@implementation JFOperation
/**
重寫(xiě)mian方法
*/
- (void)main {
if (!self.isCancelled) {
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}
}
}
@end
/**
* 使用自定義繼承自 NSOperation 的子類(lèi)
*/
- (void)useCustomOperation {
// 1.創(chuàng)建 JFOperation 對(duì)象
JFOperation *op = [[JFOperation alloc] init];
// 2.調(diào)用 start 方法開(kāi)始執(zhí)行操作
[op start];
}
NSOperationQueue的使用
NS_CLASS_AVAILABLE(10_5, 2_0)
@interface NSOperationQueue : NSObject {
@private
id _private;
void *_reserved;
}
- (void)addOperation:(NSOperation *)op;
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
- (void)addOperationWithBlock:(void (^)(void))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;
@property (readonly) NSUInteger operationCount API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
@property NSInteger maxConcurrentOperationCount;
@property (getter=isSuspended) BOOL suspended;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
@property NSQualityOfService qualityOfService API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
@property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
- (void)cancelAllOperations;
- (void)waitUntilAllOperationsAreFinished;
@property (class, readonly, strong, nullable) NSOperationQueue *currentQueue API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
@property (class, readonly, strong) NSOperationQueue *mainQueue API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
@end
NS_ASSUME_NONNULL_END
如上系統(tǒng)api所示:NSOperationQueue有一個(gè)
@property NSInteger maxConcurrentOperationCount;屬性
顧名思義 最大的并發(fā)操作數(shù)
最大并發(fā)操作數(shù):maxConcurrentOperationCount
maxConcurrentOperationCount默認(rèn)情況下為-1,表示不進(jìn)行限制,可進(jìn)行并發(fā)執(zhí)行。
maxConcurrentOperationCount為1時(shí),隊(duì)列為串行隊(duì)列。只能串行執(zhí)行。
maxConcurrentOperationCount大于1時(shí),隊(duì)列為并發(fā)隊(duì)列。操作并發(fā)執(zhí)行,當(dāng)然這個(gè)值不應(yīng)超過(guò)系統(tǒng)限制,即使自己設(shè)置一個(gè)很大的值,系統(tǒng)也會(huì)自動(dòng)調(diào)整為 min{自己設(shè)定的值,系統(tǒng)設(shè)定的默認(rèn)最大值}。
maxConcurrentOperationCount = 1

如上圖
maxConcurrentOperationCount= 1 是串行
maxConcurrentOperationCount = 2

如上圖
maxConcurrentOperationCount= 2 是并發(fā)
maxConcurrentOperationCount = 9

maxConcurrentOperationCount 也是并發(fā)
開(kāi)啟線(xiàn)程數(shù)量是由系統(tǒng)決定的,不需要我們來(lái)管理
NSOperation 操作依賴(lài)
- (void)addDependency:(NSOperation *)op;
- (void)removeDependency:(NSOperation *)op;
@property (readonly, copy) NSArray<NSOperation *> *dependencies;
/**
* 操作依賴(lài)
* 使用方法:addDependency:
*/
- (void)addDependency {
// 1.創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.創(chuàng)建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
// 3.添加依賴(lài)
[op2 addDependency:op1]; // 讓op2 依賴(lài)于 op1,則先執(zhí)行op1,在執(zhí)行op2
// 4.添加操作到隊(duì)列中
[queue addOperation:op1];
[queue addOperation:op2];
}

如打?。簅p2在op1后執(zhí)行
NSOperation、NSOperationQueue 線(xiàn)程間的通信
/**
* 線(xiàn)程間通信
*/
- (void)communication {
// 1.創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
// 2.添加操作
[queue addOperationWithBlock:^{
// 異步進(jìn)行耗時(shí)操作
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
// 回到主線(xiàn)程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// 進(jìn)行一些 UI 刷新等操作
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
}
}];
}];
}

打印如上這個(gè)和NSThred和GCD一樣。也是先在其他線(xiàn)程中執(zhí)行操作,等操作執(zhí)行完了之后再回到主線(xiàn)程執(zhí)行主線(xiàn)程的相應(yīng)操作


打印如上票數(shù)是錯(cuò)亂的
/**
* 線(xiàn)程安全:使用 NSLock
* 初始化彩票數(shù)量、賣(mài)票窗口(線(xiàn)程安全)、并開(kāi)始賣(mài)票
*/
- (void)initTicketStatusSave {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線(xiàn)程
self.ticketSurplusCount = 50;
self.lock = [[NSLock alloc] init]; // 初始化 NSLock 對(duì)象
// 1.創(chuàng)建 queue1,queue1 代表足球彩票售賣(mài)窗口
NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
queue1.maxConcurrentOperationCount = 1;
// 2.創(chuàng)建 queue2,queue2 代表籃球票售賣(mài)窗口
NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
queue2.maxConcurrentOperationCount = 1;
// 3.創(chuàng)建賣(mài)票操作 op1
__weak typeof(self) weakSelf = self;
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
[weakSelf saleTicketSafe];
}];
// 4.創(chuàng)建賣(mài)票操作 op2
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
[weakSelf saleTicketSafe];
}];
// 5.添加操作,開(kāi)始賣(mài)票
[queue1 addOperation:op1];
[queue2 addOperation:op2];
}
/**
* 售賣(mài)彩票票(線(xiàn)程安全)
*/
- (void)saleTicketSafe {
while (1) {
// 加鎖
[self.lock lock];
if (self.ticketSurplusCount > 0) {
//如果還有票,繼續(xù)售賣(mài)
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%ld 窗口:%@", (long)self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
}
// 解鎖
[self.lock unlock];
if (self.ticketSurplusCount <= 0) {
NSLog(@"所有彩票均已售完");
break;
}
}
}


打印如上 正是我們想要的結(jié)果
NSOperation,NSOperationQueue 的優(yōu)先級(jí)
NSOperation對(duì)象使用setQueuePriority:設(shè)置自身在NSOperationQueue對(duì)象中執(zhí)行的優(yōu)先級(jí)。參數(shù)有:
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
-(void)queuePriority{
NSBlockOperation *blkop1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"執(zhí)行blkop1");
}];
NSBlockOperation *blkop2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"執(zhí)行blkop2");
}];
// 設(shè)置操作優(yōu)先級(jí)
blkop1.queuePriority = NSOperationQueuePriorityLow;
blkop2.queuePriority = NSOperationQueuePriorityVeryHigh;
NSLog(@"blkop1 == %@",blkop1);
NSLog(@"blkop2 == %@",blkop2);
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 操作添加到隊(duì)列
[queue addOperation:blkop1];
[queue addOperation:blkop2];
NSLog(@"%@",[queue operations]);
for (NSOperation *op in [queue operations]) {
NSLog(@"op == %@",op);
}
}

注意:
- 優(yōu)先級(jí)只能應(yīng)用于相同queue中的operations。
- 操作的優(yōu)先級(jí)高低不等于操作在隊(duì)列中排列的順序。換句話(huà)說(shuō),優(yōu)先級(jí)高的操作不代表一定排在隊(duì)列的前面。后入隊(duì)的操作有可能因?yàn)閮?yōu)先級(jí)高而先被執(zhí)行。PS:操作在隊(duì)列中的順序取決于隊(duì)列的addOperation:方法。
- 優(yōu)先級(jí)高只代表先被執(zhí)行。不代表操作先被執(zhí)行完成。執(zhí)行完成的早晚還取決于操作耗時(shí)長(zhǎng)短。
- 優(yōu)先級(jí)不能替代依賴(lài),優(yōu)先級(jí)也絕不等于依賴(lài)。優(yōu)先級(jí)只是對(duì)已經(jīng)準(zhǔn)備好的操作確定其執(zhí)行順序。
- 操作的執(zhí)行優(yōu)先滿(mǎn)足依賴(lài)關(guān)系,然后再滿(mǎn)足優(yōu)先級(jí)。即先根據(jù)依賴(lài)執(zhí)行操作,然后再?gòu)乃袦?zhǔn)備好的操作中取出優(yōu)先級(jí)最高的那一個(gè)執(zhí)行。
好了 NSOperation 的分享就到這里 下篇將會(huì)給大家?guī)?lái)NSRunloop大家哪里不清楚的 ,有啥想了解的 在評(píng)論區(qū)評(píng)論。