緊接著上一篇GCD 之后 今天給大家 分享和總結的是NSOperation
廢話不多說:來看看NSOperation 是什么鬼日杈?
官網(wǎng)的解釋是:
翻譯:一個抽象類蘸泻,表示與單個任務關聯(lián)的代碼和數(shù)據(jù)
如果你有閱讀過 SDWebImage和AFNetworking的源碼的話 那么你就會注意到里面大量的用到了大量的 NSOperation 為什么呢碉纳?
原因:
NSOperation裙戏、NSOperationQueue 是基于 GCD 更高一層的封裝厉萝,完全面向對象屁商。但是比 GCD 更簡單易用、代碼可讀性也更高七问。
- 可添加完成的代碼塊蜓耻,在操作完成后執(zhí)行。
- 添加操作之間的依賴關系械巡,方便的控制執(zhí)行順序刹淌。
- 設定操作執(zhí)行的優(yōu)先級。
- 可以很方便的取消一個操作的執(zhí)行。
- 使用 KVO 觀察對操作執(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)建,但是可以使用它的子類NSBlockOperation
和NSInvocationOperation
,前者是使用Block的方式,使用起來比較方便。
NSBlockOperation
/**
* 使用子類 NSBlockOperation
*/
- (void)useBlockOperation {
// 1.創(chuàng)建 NSBlockOperation 對象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程
}
}];
// 2.調用 start 方法開始執(zhí)行操作
[op start];
}
可以看出NSBlockOperation
默認在主線程中執(zhí)行
看下addExecutionBlock
/**
* 使用子類 NSBlockOperation
* 調用方法 AddExecutionBlock:
*/
- (void)useBlockOperationAddExecutionBlock {
// 1.創(chuàng)建 NSBlockOperation 對象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程
}
}];
// 2.添加額外的操作
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"3---%@", [NSThread currentThread]); // 打印當前線程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"4---%@", [NSThread currentThread]); // 打印當前線程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"5---%@", [NSThread currentThread]); // 打印當前線程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"6---%@", [NSThread currentThread]); // 打印當前線程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"7---%@", [NSThread currentThread]); // 打印當前線程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"8---%@", [NSThread currentThread]); // 打印當前線程
}
}];
// 3.調用 start 方法開始執(zhí)行操作
[op start];
}
// 1.創(chuàng)建 NSBlockOperation 對象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程
}
}];
可以看出上面是在主線程中執(zhí)行
使用自定義繼承自 NSOperation 的子類
@implementation JFOperation
/**
重寫mian方法
*/
- (void)main {
if (!self.isCancelled) {
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}
}
}
@end
/**
* 使用自定義繼承自 NSOperation 的子類
*/
- (void)useCustomOperation {
// 1.創(chuàng)建 JFOperation 對象
JFOperation *op = [[JFOperation alloc] init];
// 2.調用 start 方法開始執(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
有一個
@property NSInteger maxConcurrentOperationCount;
屬性
顧名思義 最大的并發(fā)操作數(shù)
最大并發(fā)操作數(shù):maxConcurrentOperationCount
maxConcurrentOperationCount
默認情況下為-1,表示不進行限制雇逞,可進行并發(fā)執(zhí)行。
maxConcurrentOperationCount
為1時茁裙,隊列為串行隊列塘砸。只能串行執(zhí)行。
maxConcurrentOperationCount
大于1時晤锥,隊列為并發(fā)隊列谣蠢。操作并發(fā)執(zhí)行,當然這個值不應超過系統(tǒng)限制,即使自己設置一個很大的值眉踱,系統(tǒng)也會自動調整為 min{自己設定的值挤忙,系統(tǒng)設定的默認最大值}。
maxConcurrentOperationCount = 1
如上圖
maxConcurrentOperationCount
= 1 是串行
maxConcurrentOperationCount = 2
如上圖
maxConcurrentOperationCount
= 2 是并發(fā)
maxConcurrentOperationCount = 9
maxConcurrentOperationCount
也是并發(fā)
開啟線程數(shù)量是由系統(tǒng)決定的谈喳,不需要我們來管理
NSOperation 操作依賴
- (void)addDependency:(NSOperation *)op;
- (void)removeDependency:(NSOperation *)op;
@property (readonly, copy) NSArray<NSOperation *> *dependencies;
/**
* 操作依賴
* 使用方法:addDependency:
*/
- (void)addDependency {
// 1.創(chuàng)建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.創(chuàng)建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程
}
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程
}
}];
// 3.添加依賴
[op2 addDependency:op1]; // 讓op2 依賴于 op1册烈,則先執(zhí)行op1,在執(zhí)行op2
// 4.添加操作到隊列中
[queue addOperation:op1];
[queue addOperation:op2];
}
如打有銮荨:op2在op1后執(zhí)行
NSOperation赏僧、NSOperationQueue 線程間的通信
/**
* 線程間通信
*/
- (void)communication {
// 1.創(chuàng)建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
// 2.添加操作
[queue addOperationWithBlock:^{
// 異步進行耗時操作
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程
}
// 回到主線程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// 進行一些 UI 刷新等操作
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程
}
}];
}];
}
打印如上這個和NSThred和GCD一樣。也是先在其他線程中執(zhí)行操作扭倾,等操作執(zhí)行完了之后再回到主線程執(zhí)行主線程的相應操作
打印如上票數(shù)是錯亂的
/**
* 線程安全:使用 NSLock
* 初始化彩票數(shù)量淀零、賣票窗口(線程安全)、并開始賣票
*/
- (void)initTicketStatusSave {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程
self.ticketSurplusCount = 50;
self.lock = [[NSLock alloc] init]; // 初始化 NSLock 對象
// 1.創(chuàng)建 queue1,queue1 代表足球彩票售賣窗口
NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
queue1.maxConcurrentOperationCount = 1;
// 2.創(chuàng)建 queue2,queue2 代表籃球票售賣窗口
NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
queue2.maxConcurrentOperationCount = 1;
// 3.創(chuàng)建賣票操作 op1
__weak typeof(self) weakSelf = self;
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
[weakSelf saleTicketSafe];
}];
// 4.創(chuàng)建賣票操作 op2
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
[weakSelf saleTicketSafe];
}];
// 5.添加操作膛壹,開始賣票
[queue1 addOperation:op1];
[queue2 addOperation:op2];
}
/**
* 售賣彩票票(線程安全)
*/
- (void)saleTicketSafe {
while (1) {
// 加鎖
[self.lock lock];
if (self.ticketSurplusCount > 0) {
//如果還有票驾中,繼續(xù)售賣
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;
}
}
}
打印如上 正是我們想要的結果
NSOperation,NSOperationQueue 的優(yōu)先級
NSOperation對象使用setQueuePriority:設置自身在NSOperationQueue對象中執(zhí)行的優(yōu)先級模聋。參數(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");
}];
// 設置操作優(yōu)先級
blkop1.queuePriority = NSOperationQueuePriorityLow;
blkop2.queuePriority = NSOperationQueuePriorityVeryHigh;
NSLog(@"blkop1 == %@",blkop1);
NSLog(@"blkop2 == %@",blkop2);
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 操作添加到隊列
[queue addOperation:blkop1];
[queue addOperation:blkop2];
NSLog(@"%@",[queue operations]);
for (NSOperation *op in [queue operations]) {
NSLog(@"op == %@",op);
}
}
注意:
- 優(yōu)先級只能應用于相同queue中的operations肩民。
- 操作的優(yōu)先級高低不等于操作在隊列中排列的順序。換句話說链方,優(yōu)先級高的操作不代表一定排在隊列的前面持痰。后入隊的操作有可能因為優(yōu)先級高而先被執(zhí)行。PS:操作在隊列中的順序取決于隊列的addOperation:方法祟蚀。
- 優(yōu)先級高只代表先被執(zhí)行工窍。不代表操作先被執(zhí)行完成。執(zhí)行完成的早晚還取決于操作耗時長短前酿。
- 優(yōu)先級不能替代依賴移剪,優(yōu)先級也絕不等于依賴。優(yōu)先級只是對已經準備好的操作確定其執(zhí)行順序薪者。
- 操作的執(zhí)行優(yōu)先滿足依賴關系,然后再滿足優(yōu)先級剿涮。即先根據(jù)依賴執(zhí)行操作言津,然后再從所有準備好的操作中取出優(yōu)先級最高的那一個執(zhí)行。
好了 NSOperation 的分享就到這里 下篇將會給大家?guī)?code>NSRunloop大家哪里不清楚的 取试,有啥想了解的 在評論區(qū)評論悬槽。