iOS多線(xiàn)程之NSOperation

緊接著上一篇GCD 之后 今天給大家 分享和總結(jié)的是NSOperation

廢話(huà)不多說(shuō):來(lái)看看NSOperation 是什么鬼?
官網(wǎng)的解釋是:


image.png

翻譯:一個(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)NSBlockOperationNSInvocationOperation,前者是使用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];
}

image.png

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

image.png

看下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];
}
image.png
 // 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

image.png

如上圖maxConcurrentOperationCount= 1 是串行

maxConcurrentOperationCount = 2

image.png

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

maxConcurrentOperationCount = 9

image.png

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];
}
image.png

如打?。簅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)程
            }
        }];
    }];
}
image.png

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

image.png
image.png

打印如上票數(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;
        }
}
}
image.png
image.png

打印如上 正是我們想要的結(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);
    }
    
}

image.png

注意:

  • 優(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)論。

demo地址:https://github.com/tubie/JFMultiThreading

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 垂直導(dǎo)航條 1.效果 2.html代碼:利用無(wú)序列表創(chuàng)建導(dǎo)航 3.css 先清除距離,去除列表樣式,設(shè)置顏色邊框樣...
    SpareNoEfforts閱讀 606評(píng)論 0 0
  • 最近忙來(lái)忙去,回到宿舍里的時(shí)間甚少,閉上眼睛往床上一倒,也顧不上什么地上灰塵頭發(fā),桌上廢紙雜物了,果不其然,沒(méi)挨過(guò)...
    林青青l(xiāng)in閱讀 310評(píng)論 0 0
  • 1. 自從被別人以忙碌為借口打發(fā)掉之后,凡事,我便不愿意再找忙碌的借口。 寫(xiě)了這么久,千字只需要20分鐘的我,再加...
    安之騰閱讀 837評(píng)論 50 29

友情鏈接更多精彩內(nèi)容