iOS NSOperation學(xué)習(xí)及總結(jié)

這篇文章對iOS多線程技術(shù)NSOperation的常用方法做了簡單總結(jié)
GCD請見這篇
本文代碼

NSOperation:

- 簡介:

  • 是蘋果在GCD的基礎(chǔ)上做了一次面向?qū)ο蟮姆庋b
  • 核心概念和GCD很像
  • NSOperation是一個抽象類,想要封裝操作,需要使用子類
  • 可以自定義子類,繼承NSOperation,實(shí)現(xiàn)多線程操作

- NSOperation的子類:

  • NSInvocationOperation:
    NSInvocationOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
    [op start];

打印結(jié)果:

------<NSThread: 0x600000260ac0>{number = 1, name = main}

結(jié)論:
如果僅僅是封裝了操作,然后調(diào)用start方法
他是不會開線程的
就像調(diào)用了performSelectorr一樣
想要開線程,必須把任務(wù)加到隊(duì)列中去
因?yàn)镹SOperation底層是GCD,GCD就是把任務(wù)加入到隊(duì)列,根據(jù)隊(duì)列和函數(shù)的情況來決定是否開啟線程

  • NSBlockOperation:
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"下載1------%@", [NSThread currentThread]);
    }];
    //添加額外任務(wù)
    [op addExecutionBlock:^{
        NSLog(@"下載2------%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"下載3------%@", [NSThread currentThread]);
    }];
    [op start];

打印結(jié)果:

下載1------<NSThread: 0x60800006f180>{number = 1, name = main}
下載2------<NSThread: 0x6000000791c0>{number = 3, name = (null)}
下載3------<NSThread: 0x608000079e00>{number = 4, name = (null)}

結(jié)論:
和NSInvocationOperation一樣
如果只是封裝了操作并調(diào)用了start方法
只是會在主線程執(zhí)行任務(wù)
但是如果通過addExecutionBlock添加了新任務(wù)
那么新任務(wù)會在子線程執(zhí)行(這里是比較特殊的地方)

- NSOperationQueue:

NSOperation可以用start方法來執(zhí)行任務(wù),但是默認(rèn)是同步的
如果將NSOperation添加到NSOperationQueue中,系統(tǒng)會自動異步執(zhí)行NSOperation中的操作

    //創(chuàng)建隊(duì)列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    //創(chuàng)建操作(NSInvocationOperation)
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download2) object:nil];
    
    //創(chuàng)建操作(NSBlockOperation)
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3 --- %@", [NSThread currentThread]);
    }];
    [op3 addExecutionBlock:^{
        NSLog(@"download4 --- %@", [NSThread currentThread]);
    }];
    [op3 addExecutionBlock:^{
        NSLog(@"download5 --- %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download6 --- %@", [NSThread currentThread]);
    }];
    
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];

打印結(jié)果:

download6 --- <NSThread: 0x608000269080>{number = 6, name = (null)}
download2 --- <NSThread: 0x600000265240>{number = 4, name = (null)}
download1 --- <NSThread: 0x600000265200>{number = 3, name = (null)}
download3 --- <NSThread: 0x6000002655c0>{number = 5, name = (null)}
download4 --- <NSThread: 0x608000265f80>{number = 7, name = (null)}
download5 --- <NSThread: 0x600000265300>{number = 8, name = (null)}

結(jié)論:
NSOperation有兩種隊(duì)列:

  • 主隊(duì)列:
[NSOperationQueue mainQueue];

凡是添加到主隊(duì)列中的任務(wù),都會在主線程執(zhí)行

  • 非主隊(duì)列
[[NSOperationQueue alloc]init];

同時包含了串行和并發(fā)的功能
就像上面的代碼,把任務(wù)添加到隊(duì)列以后,自動在子線程中執(zhí)行
如果想用非主線程實(shí)現(xiàn)串行,則需要設(shè)置并發(fā)量

- 其他用法:

  • 最大并發(fā)數(shù):
// 設(shè)置最大并發(fā)操作數(shù)
    queue.maxConcurrentOperationCount = 2;

設(shè)置了最大并發(fā)數(shù),就是限制了系統(tǒng)開線程的數(shù)量
如果設(shè)置為1
任務(wù)就會串行執(zhí)行

  • 掛起:
self.queue.suspended = YES;

當(dāng)隊(duì)列中的任務(wù)正在執(zhí)行的時候,設(shè)置掛起為YES
任務(wù)會暫停
但是沒有從內(nèi)存中銷毀
當(dāng)設(shè)置為NO的時候,任務(wù)會繼續(xù)進(jìn)行

  • 取消所有任務(wù):
[self.queue cancelAllOperations];

調(diào)用queue的cancelAllOperations方法
相當(dāng)于調(diào)用了queue中每個NSOperation的cancel方法
所有的任務(wù)就被取消了
如果想繼續(xù)任務(wù)
需要重新定制任務(wù)加入隊(duì)列
但是需要注意的是:
如果調(diào)用cancel方法的時候,NSOperation有一個任務(wù)正在執(zhí)行
那么需要這個任務(wù)執(zhí)行完了以后,再實(shí)現(xiàn)cancel

  • 自定義NSOperation:

繼承NSOperation類
可以實(shí)現(xiàn)自定義NSOperation
需要執(zhí)行的任務(wù)在main方法中實(shí)現(xiàn)

#import "HXOperation.h"
@implementation HXOperation
/**
 * 需要執(zhí)行的任務(wù)
 */
-(void)main
{
    for (NSInteger i = 0; i<1000; i++) {
        NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
    }
    if (self.isCancelled) return;
    for (NSInteger i = 0; i<1000; i++) {
        NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]);
    }
    if (self.isCancelled) return;
    for (NSInteger i = 0; i<1000; i++) {
        NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]);
    }
    if (self.isCancelled) return;
}
@end

在外部這么使用就可以:

    // 創(chuàng)建隊(duì)列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //添加NSOperation
    [queue addOperation:[[HXOperation alloc] init]];

這樣就會自動執(zhí)行main中的任務(wù)了
但是蘋果建議我們自定義NSOperation的時候,如果內(nèi)部有耗時操作
那么應(yīng)該在每一個耗時操作結(jié)束以后,檢查一下當(dāng)前任務(wù)有沒有被取消
因?yàn)楹苡锌赡躈SOperation在執(zhí)行任務(wù)的時候,外部調(diào)用了他的cancel方法
如果被取消了就不要再執(zhí)行剩下的任務(wù)了:

if (self.isCancelled) return;
  • 任務(wù)依賴和任務(wù)監(jiān)聽

我們知道把多個任務(wù)加入隊(duì)列后
系統(tǒng)從隊(duì)列中把任務(wù)取出來并發(fā)執(zhí)行
但是誰先執(zhí)行取決于CPU先調(diào)度那條線程
當(dāng)我們需要執(zhí)行某個任務(wù)之后再執(zhí)行其他任務(wù)的話(比如下載完圖片后再對圖片進(jìn)行處理)
那么就需要設(shè)置依賴
或者可以對任務(wù)做監(jiān)聽

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download1----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download2----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"download4----%@", [NSThread  currentThread]);
        }
    }];
    NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download5----%@", [NSThread  currentThread]);
    }];
    //監(jiān)聽任務(wù)執(zhí)行完畢后 進(jìn)行其他操作
    op5.completionBlock = ^{
        NSLog(@"op5執(zhí)行完畢---%@", [NSThread currentThread]);
    };
    
    // 設(shè)置依賴
    [op3 addDependency:op1];
    [op3 addDependency:op2];
    [op3 addDependency:op4];

    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];
  • 線程之間的通信:

在子線程下載圖片后,回到主線程更新UI:

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    __block UIImage *image1 = nil;
    // 下載圖片1
    NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
        // 圖片的網(wǎng)絡(luò)路徑
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        // 加載圖片
        NSData *data = [NSData dataWithContentsOfURL:url];
        // 生成圖片
        image1 = [UIImage imageWithData:data];
    }];
    __block UIImage *image2 = nil;
    // 下載圖片2
    NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
         // 圖片的網(wǎng)絡(luò)路徑
        NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
        // 加載圖片
        NSData *data = [NSData dataWithContentsOfURL:url];
        // 生成圖片
        image2 = [UIImage imageWithData:data];
    }];
    // 合成圖片
    NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
        // 開啟新的圖形上下文
        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
        // 繪制圖片
        [image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        image1 = nil;
        [image2 drawInRect:CGRectMake(50, 0, 50, 100)];
        image2 = nil;
        // 取得上下文中的圖片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        // 結(jié)束上下文
        UIGraphicsEndImageContext();
        // 回到主線程顯示圖片
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
    //設(shè)置依賴 保證下載完圖片 再去合成
    [combine addDependency:download1];
    [combine addDependency:download2];
    
    [queue addOperation:download1];
    [queue addOperation:download2];
    [queue addOperation:combine];

感謝閱讀
你的支持是我寫作的唯一動力

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

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

  • NSThread 第一種:通過NSThread的對象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 947評論 0 3
  • 在這篇文章中,我將為你整理一下 iOS 開發(fā)中幾種多線程方案,以及其使用方法和注意事項(xiàng)。當(dāng)然也會給出幾種多線程的案...
    張戰(zhàn)威ican閱讀 691評論 0 0
  • 多線程基本概念 單核CPU,同一時間cpu只能處理1個線程,只有1個線程在執(zhí)行 。多線程同時執(zhí)行:是CPU快速的在...
    WeiHing閱讀 780評論 1 5
  • 作業(yè)1. 好好回想一下,小時候呆呆看過什么。挑選一個印象或一幅畫面寫出來。 小時候呆呆看過什么已經(jīng)想不起了,不過最...
    狐皮克閱讀 232評論 2 0
  • 找一個懂你的人和找一個愛你的人不是一回事。 懂你什么呢,愛你可以是無條件的,懂你不僅不可能,要“懂”也是有條件的。...
    子俠閱讀 679評論 1 3

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