NSOperation 的使用,以及與GCD優(yōu)缺點

1. NSOperation簡介

NSOperation是蘋果提供給我們的一套多線程解決方案卡儒。實際上NSOperation是基于GCD更高一層的封裝,但是比GCD更簡單易用辈讶、代碼可讀性也更高摊腋。

NSOperation需要配合NSOperationQueue來實現(xiàn)多線程辖众。因為默認(rèn)情況下,NSOperation單獨使用時系統(tǒng)同步執(zhí)行操作载荔,并沒有開辟新線程的能力盾饮,只有配合NSOperationQueue才能實現(xiàn)異步執(zhí)行。

因為NSOperation是基于GCD的懒熙,那么使用起來也和GCD差不多丘损,其中,NSOperation相當(dāng)于GCD中的任務(wù)工扎,而NSOperationQueue則相當(dāng)于GCD中的隊列徘钥。NSOperation實現(xiàn)多線程的使用步驟分為三步:

1 .創(chuàng)建任務(wù):先將需要執(zhí)行的操作封裝到一個NSOperation對象中。
2 .創(chuàng)建隊列:創(chuàng)建NSOperationQueue對象肢娘。
3 .將任務(wù)加入到隊列中:然后將NSOperation對象添加到NSOperationQueue中呈础。
之后呢,系統(tǒng)就會自動將NSOperationQueue中的NSOperation取出來橱健,在新線程中執(zhí)行操作而钞。

2.NSOperation和NSOperationQueue的基本使用

創(chuàng)建任務(wù)
NSOperation是個抽象類,并不能封裝任務(wù)拘荡。我們只有使用它的子類來封裝任務(wù)臼节。我們有三種方式來封裝任務(wù)。

使用子類NSInvocationOperation
使用子類NSBlockOperation
定義繼承自NSOperation的子類珊皿,通過實現(xiàn)內(nèi)部相應(yīng)的方法來封裝任務(wù)网缝。
在不使用NSOperationQueue,單獨使用NSOperation的情況下系統(tǒng)同步執(zhí)行操作蟋定,下面我們學(xué)習(xí)以下任務(wù)的三種創(chuàng)建方式粉臊。

使用子類- NSInvocationOperation

// 使用子類- NSInvocationOperation:

- (void)NSInvocationOperationDemo{

    // 1.創(chuàng)建NSInvocationOperation對象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

    // 2.調(diào)用start方法開始執(zhí)行操作
    [op start];
}

- (void)run
{
    NSLog(@"當(dāng)前的線程為--%@", [NSThread currentThread]);
}

運行結(jié)果如下:

當(dāng)前的線程為--<NSThread: 0x60800007ff00>{number = 1, name = main}

從中可以看到,在沒有使用NSOperationQueue溢吻、單獨使用NSInvocationOperation的情況下维费,NSInvocationOperation在主線程執(zhí)行操作,并沒有開啟新線程促王。

下邊再來看看NSBlockOperation犀盟。

2 :使用子類-NSBlockOperation
// 使用 -NSBlockOperation

- (void)NSBlockOperationDemo{

    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"-當(dāng)前的線程為-%@",[NSThread currentThread]);
    }];

    [op start];
}

結(jié)果如下:

 -當(dāng)前的線程為-<NSThread: 0x60800007cac0>{number = 1, name = main}

我們同樣可以看到,在沒有使用NSOperationQueue蝇狼、單獨使用NSBlockOperation的情況下阅畴,NSBlockOperation也是在主線程執(zhí)行操作,并沒有開啟新線程迅耘。

3 :使用NSBlockOperation 的addExecutionBlock操作

// addExecutionBlock
- (void)blockOperation
{
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        // 在主線程
        NSLog(@"1------%@", [NSThread currentThread]);
    }];

    // 添加額外的任務(wù)(在子線程執(zhí)行)
    [op addExecutionBlock:^{
        NSLog(@"2------%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"3------%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"4------%@", [NSThread currentThread]);
    }];

    [op start];
}

可以看出贱枣,blockOperationWithBlock:方法中的操作是在主線程中執(zhí)行的监署,而addExecutionBlock:方法中的操作是在其他線程中執(zhí)行的。

1------<NSThread: 0x608000077f80>{number = 1, name = main}
 3------<NSThread: 0x600000264600>{number = 3, name = (null)}
 2------<NSThread: 0x6080002614c0>{number = 5, name = (null)}
 4------<NSThread: 0x600000265780>{number = 4, name = (null)}
4. 定義繼承自NSOperation的子類

先定義一個繼承自NSOperation的子類纽哥,重寫main方法
YSCOperation.h

#import <Foundation/Foundation.h>

@interface YSCOperation : NSOperation

@end

YSCOperation.m

#import "YSCOperation.h"

@implementation YSCOperation
/**
 * 需要執(zhí)行的任務(wù)
 */

- (void)main
{
    for (int i = 0; i < 2; ++i) {
        NSLog(@"--i---%@",[NSThread currentThread]);
    }    
}

@end

然后使用的時候?qū)腩^文件YSCOperation.h

// 創(chuàng)建YSCOperation
YSCOperation *op1 = [[YSCOperation alloc] init];

[op1 start];

運行后的輸出結(jié)果如下:

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

可以看出:在沒有使用NSOperationQueue钠乏、單獨使用自定義子類的情況下,是在主線程執(zhí)行操作春塌,并沒有開啟新線程晓避。

下邊我們簡單講講NSOperationQueue的創(chuàng)建。

2. 創(chuàng)建隊列

和GCD中的并發(fā)隊列只壳、串行隊列略有不同的是:NSOperationQueue一共有兩種隊列:主隊列俏拱、其他隊列。其中其他隊列同時包含了串行吼句、并發(fā)功能锅必。下邊是主隊列、其他隊列的基本創(chuàng)建方法和特點惕艳。

主隊列

凡是添加到主隊列中的任務(wù)(NSOperation)搞隐,都會放到主線程中執(zhí)行

NSOperationQueue *queue = [NSOperationQueue mainQueue];
其他隊列(非主隊列)

添加到這種隊列中的任務(wù)(NSOperation),就會自動放到子線程中執(zhí)行
同時包含了:串行远搪、并發(fā)功能

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
3. 將任務(wù)加入到隊列中

前邊說了尔许,NSOperation需要配合NSOperationQueue來實現(xiàn)多線程。
那么我們需要將創(chuàng)建好的任務(wù)加入到隊列中去终娃∥独龋總共有兩種方法

<1> :- (void)addOperation:(NSOperation *)op;

需要先創(chuàng)建任務(wù),再將創(chuàng)建好的任務(wù)加入到創(chuàng)建好的隊列中去

- (void)addOperationToQueue
{
    // 1.創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2. 創(chuàng)建操作  
    // 創(chuàng)建NSInvocationOperation    
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];    
    // 創(chuàng)建NSBlockOperation    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1-----%@", [NSThread currentThread]);
        }
    }];

    // 3. 添加操作到隊列中:addOperation:   
    [queue addOperation:op1]; // [op1 start]    
    [queue addOperation:op2]; // [op2 start]
}

- (void)run
{
    for (int i = 0; i < 2; ++i) {
        NSLog(@"2-----%@", [NSThread currentThread]);
    }
}

運行結(jié)果如下:

2017-02-25 13:05:48.301 NSOperationDemo[1327:91464] 1--0p2---<NSThread: 0x608000066c80>{number = 3, name = (null)}
2017-02-25 13:05:48.301 NSOperationDemo[1327:91466] 2--op1---<NSThread: 0x6000000714c0>{number = 4, name = (null)}
2017-02-25 13:05:48.302 NSOperationDemo[1327:91464] 1--0p2---<NSThread: 0x608000066c80>{number = 3, name = (null)}
2017-02-25 13:05:48.302 NSOperationDemo[1327:91466] 2--op1---<NSThread: 0x6000000714c0>{number = 4, name = (null)}

可以看出:NSInvocationOperation和NSOperationQueue結(jié)合后能夠開啟新線程棠耕,進(jìn)行并發(fā)執(zhí)行NSBlockOperation和NSOperationQueue也能夠開啟新線程余佛,進(jìn)行并發(fā)執(zhí)行

<2>: - (void)addOperationWithBlock:(void (^)(void))block;

無需先創(chuàng)建任務(wù),在block中添加任務(wù)窍荧,直接將任務(wù)block加入到隊列中辉巡。

- (void)addOperationWithBlockToQueue
{
    // 1. 創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2. 添加操作到隊列中:addOperationWithBlock:
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"-----%@", [NSThread currentThread]);
        }
    }];
}

輸出結(jié)果如下:

輸出結(jié)果:
2016-09-05 17:10:47.023 NSOperationQueue[16293:2457487] -----<NSThread: 0x7ffa6bc0e1e0>{number = 2, name = (null)}
2016-09-05 17:10:47.024 NSOperationQueue[16293:2457487] -----<NSThread: 0x7ffa6bc0e1e0>{number = 2, name = (null)}

可以看出addOperationWithBlock:和NSOperationQueue能夠開啟新線程,進(jìn)行并發(fā)執(zhí)行蕊退。

3. 控制串行執(zhí)行和并行執(zhí)行的關(guān)鍵

之前我們說過郊楣,NSOperationQueue創(chuàng)建的其他隊列同時具有串行、并發(fā)功能瓤荔,上邊我們演示了并發(fā)功能净蚤,那么他的串行功能是如何實現(xiàn)的?

這里有個關(guān)鍵參數(shù)maxConcurrentOperationCount输硝,叫做最大并發(fā)數(shù)今瀑。

最大并發(fā)數(shù):maxConcurrentOperationCount

一定要在操作添加到隊列之前設(shè)置操作之間的依賴,否則操作已經(jīng)添加到隊列中在設(shè)置依賴,依賴不會生效橘荠。

maxConcurrentOperationCount默認(rèn)為-1屿附,代表不限制。

問題:默認(rèn)情況下哥童,操作隊列中的操作的執(zhí)行順序真的是無序的嗎挺份?
個人認(rèn)為,默認(rèn)情況下贮懈,操作隊列中的操作執(zhí)行順序就是其被取出的順序压恒,也是其被添加到隊列中的順序,操作的執(zhí)行順序是有序的错邦,但是操作執(zhí)行完成的順序是無序的。也就是說型宙,因為不同的操作執(zhí)行完成所需要的時間不同撬呢,最先從對壘中取出執(zhí)行的操作不一定先執(zhí)行完成,后執(zhí)行的操作不一定后執(zhí)行完成妆兑。所以魂拦,給人的感覺就是操作的執(zhí)行是無序的。
其實搁嗓,操作的依賴特性可以用GCD的信號量機制來實現(xiàn)芯勘。

當(dāng)maxConcurrentOperationCount為1時,進(jìn)行串行執(zhí)行,就是操作的執(zhí)行順序和添加順序是一致的

當(dāng)maxConcurrentOperationCount大于1時腺逛,進(jìn)行并發(fā)執(zhí)行荷愕,當(dāng)然這個值不應(yīng)超過系統(tǒng)限制,即使自己設(shè)置一個很大的值棍矛,系統(tǒng)也會自動調(diào)整安疗。

如果希望操作在主線程中執(zhí)行,不要設(shè)置maxConcurrentOperationCount = 0够委。直接把操作添加到mainQueue中即可荐类。

- (void)opetationQueue
{
    // 創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 設(shè)置最大并發(fā)操作數(shù)
    //    queue.maxConcurrentOperationCount = 2;
    queue.maxConcurrentOperationCount = 1; // 就變成了串行隊列

    // 添加操作
    [queue addOperationWithBlock:^{
        NSLog(@"1-----%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.01];
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"2-----%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.01];
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"3-----%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.01];
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"4-----%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.01];
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"5-----%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.01];
    }];

    [queue addOperationWithBlock:^{
        NSLog(@"6-----%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:0.01];
    }];
}

打印結(jié)果如下:

2017-02-25 13:17:00.942 NSOperationDemo[1455:99802] 1-----<NSThread: 0x600000262b80>{number = 3, name = (null)}
2017-02-25 13:17:00.955 NSOperationDemo[1455:99802] 2-----<NSThread: 0x600000262b80>{number = 3, name = (null)}
2017-02-25 13:17:00.966 NSOperationDemo[1455:99802] 3-----<NSThread: 0x600000262b80>{number = 3, name = (null)}
2017-02-25 13:17:00.977 NSOperationDemo[1455:99802] 4-----<NSThread: 0x600000262b80>{number = 3, name = (null)}
2017-02-25 13:17:00.989 NSOperationDemo[1455:99805] 5-----<NSThread: 0x60800026c280>{number = 4, name = (null)}
2017-02-25 13:17:01.001 NSOperationDemo[1455:99805] 6-----<NSThread: 0x60800026c280>{number = 4, name = (null)}

如果將 queue.maxConcurrentOperationCount = 2; // 就變成了并發(fā)隊列,就是操作的執(zhí)行順序不會和添加的順序不一樣,

2017-02-25 13:19:54.141 NSOperationDemo[1482:102232] 2-----<NSThread: 0x60000006c2c0>{number = 4, name = (null)}
2017-02-25 13:19:54.141 NSOperationDemo[1482:102233] 1-----<NSThread: 0x608000077fc0>{number = 3, name = (null)}
2017-02-25 13:19:54.152 NSOperationDemo[1482:102235] 3-----<NSThread: 0x600000076000>{number = 5, name = (null)}
2017-02-25 13:19:54.152 NSOperationDemo[1482:102233] 4-----<NSThread: 0x608000077fc0>{number = 3, name = (null)}
2017-02-25 13:19:54.162 NSOperationDemo[1482:102232] 5-----<NSThread: 0x60000006c2c0>{number = 4, name = (null)}
2017-02-25 13:19:54.164 NSOperationDemo[1482:102233] 6-----<NSThread: 0x608000077fc0>{number = 3, name = (null)}

可以看出:當(dāng)最大并發(fā)數(shù)為1時,任務(wù)是按順序串行執(zhí)行的茁帽。當(dāng)最大并發(fā)數(shù)為2時玉罐,任務(wù)是并發(fā)執(zhí)行的。queue.maxConcurrentOperationCount 的值越大,開啟的線程會偏多,不過,開啟線程數(shù)量是由系統(tǒng)決定的潘拨,當(dāng)然這個值不應(yīng)超過系統(tǒng)限制吊输,即使自己設(shè)置一個很大的值,系統(tǒng)也會自動調(diào)整

4. 操作依賴

NSOperation和NSOperationQueue最吸引人的地方是它能添加操作之間的依賴關(guān)系铁追。比如說有A璧亚、B兩個操作,其中A執(zhí)行完操作,B才能執(zhí)行操作癣蟋,那么就需要讓B依賴于A透硝。具體如下:

- (void)addDependency
{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1-----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2-----%@", [NSThread  currentThread]);
    }];

    [op2 addDependency:op1];    // 讓op2 依賴于 op1,則先執(zhí)行op1疯搅,在執(zhí)行op2

    [queue addOperation:op1];
    [queue addOperation:op2];
}

輸出結(jié)果:

2016-09-05 17:51:28.811 操作依賴[16423:2484866] 1-----<NSThread: 0x7fc138e1e7c0>{number = 2, name = (null)}
2016-09-05 17:51:28.812 操作依賴[16423:2484866] 2-----<NSThread: 0x7fc138e1e7c0>{number = 2, name = (null)}

可以看到濒生,無論運行幾次,其結(jié)果都是op1先執(zhí)行幔欧,op2后執(zhí)行罪治。

5. 一些其他方法
- (void)cancel;// NSOperation提供的方法,可取消單個操作
- (void)cancelAllOperations;// NSOperationQueue提供的方法礁蔗,可以取消隊列的所有操作
- (void)setSuspended:(BOOL)b;// 可設(shè)置任務(wù)的暫停和恢復(fù)觉义,YES代表暫停隊列,NO代表恢復(fù)隊列
- (BOOL)isSuspended;// 判斷暫停狀態(tài)

注意:

這里的暫停和取消并不代表可以將當(dāng)前的操作立即取消浴井,而是當(dāng)當(dāng)前的操作執(zhí)行完畢之后不再執(zhí)行新的操作晒骇。
暫停和取消的區(qū)別就在于:暫停操作之后還可以恢復(fù)操作,繼續(xù)向下執(zhí)行磺浙;而取消操作之后洪囤,所有的操作就清空了,無法再接著執(zhí)行剩下的操作撕氧。

6 .NSOperation優(yōu)先級

GCD中瘤缩,任務(wù)(block)是沒有優(yōu)先級的,而隊列具有優(yōu)先級伦泥。和GCD相反剥啤,我們一般考慮 NSOperation 的優(yōu)先級

NSOperation 有一個NSOperationQueuePriority 枚舉類型的屬性 queuePriority

public enum NSOperationQueuePriority : Int {  
    case VeryLow
    case Low
    case Normal
    case High
    case VeryHigh
}

需要注意的是,NSOperationQueue 也不能完全保證優(yōu)先級高的任務(wù)一定先執(zhí)行不脯。
queuePriority默認(rèn)值是NSOperationQueuePriorityNormal铐殃。根據(jù)實際需要我們可以通過調(diào)用queuePriority的setter方法修改某個操作的優(yōu)先級。
NSBlockOperation *blkop1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"執(zhí)行blkop1");
}];

NSBlockOperation *blkop2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"執(zhí)行blkop2");
}];

// 設(shè)置操作優(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);
}

// 輸出結(jié)果:

2017-02-12 19:36:01.149 NSOperation[1712:177976] blkop1 == <NSBlockOperation: 0x608000044440>
2017-02-12 19:36:01.150 NSOperation[1712:177976] blkop2 == <NSBlockOperation: 0x6080000444d0>
2017-02-12 19:36:01.150 NSOperation[1712:177976] (
    "<NSBlockOperation: 0x608000044440>",
    "<NSBlockOperation: 0x6080000444d0>"
)
2017-02-12 19:36:01.150 NSOperation[1712:177976] op == <NSBlockOperation: 0x608000044440>
2017-02-12 19:36:01.150 NSOperation[1712:177976] op == <NSBlockOperation: 0x6080000444d0>
2017-02-12 19:36:01.150 NSOperation[1712:178020] 執(zhí)行blkop1
2017-02-12 19:36:01.151 NSOperation[1712:178021] 執(zhí)行blkop2

解析:
(1.)上面創(chuàng)建了兩個blockOperation并且分別設(shè)置了優(yōu)先級跨新。顯然blkop1的優(yōu)先級低于blkop2的優(yōu)先級富腊。然后調(diào)用了隊列的addOperation:方法使操作入隊。最后輸出結(jié)果證明域帐,操作在對列中的順去取決于addOperation:方法而不是優(yōu)先級赘被。
(2.)雖然blkop2優(yōu)先級高于blkop1,但是bloop1卻先于blkop2執(zhí)行完成肖揣。所以民假,優(yōu)先級高的操作不一定先執(zhí)行完成。

注意:
(1.)優(yōu)先級只能應(yīng)用于相同queue中的operations龙优。
(2.)操作的優(yōu)先級高低不等于操作在隊列中排列的順序羊异。換句話說,優(yōu)先級高的操作不代表一定排在隊列的前面。后入隊的操作有可能因為優(yōu)先級高而先被執(zhí)行野舶。PS:操作在隊列中的順序取決于隊列的addOperation:方法易迹。(證明代碼如下)
(3.)優(yōu)先級高只代表先被執(zhí)行。不代表操作先被執(zhí)行完成平道。執(zhí)行完成的早晚還取決于操作耗時長短睹欲。
(4.)優(yōu)先級不能替代依賴,優(yōu)先級也絕不等于依賴一屋。優(yōu)先級只是對已經(jīng)準(zhǔn)備好的操作確定其執(zhí)行順序窘疮。
(5.)操作的執(zhí)行優(yōu)先滿足依賴關(guān)系,然后再滿足優(yōu)先級冀墨。即先根據(jù)依賴執(zhí)行操作闸衫,然后再從所有準(zhǔn)備好的操作中取出優(yōu)先級最高的那一個執(zhí)行。

7 :waitUntilAllOperationsAreFinished

為了最佳的性能,你應(yīng)該設(shè)計你的應(yīng)用盡可能地異步操作诽嘉,讓應(yīng)用在Operation正在執(zhí)行時可以去處理其它事情蔚出。如果需要在當(dāng)前線程中處理operation完成后的結(jié)果,可以使用NSOperation的waitUntilFinished方法阻塞當(dāng)前線程,等待operation完成含懊。通常我們應(yīng)該避免編寫這樣的代碼,阻塞當(dāng)前線程可能是一種簡便的解決方案,但是它引入了更多的串行代碼,限制了整個應(yīng)用的并發(fā)性,同時也降低了用戶體驗。絕對不要在應(yīng)用主線程中等待一個Operation,只能在第二或次要線程中等待衅胀。阻塞主線程將導(dǎo)致應(yīng)用無法響應(yīng)用戶事件,應(yīng)用也將表現(xiàn)為無響應(yīng)岔乔。

// 會阻塞當(dāng)前線程,等到某個operation執(zhí)行完畢  
    [operation waitUntilFinished];

除了等待單個Operation完成,你也可以同時等待一個queue中的所有操作,使用NSOperationQueue的waitUntilAllOperationsAreFinished方法滚躯。注意:在等待一個 queue時,應(yīng)用的其它線程仍然可以往queue中添加Operation,因此可能會加長線程的等待時間雏门。

// 阻塞當(dāng)前線程,等待queue的所有操作執(zhí)行完畢  
    [queue waitUntilAllOperationsAreFinished];

注意:waitUntilAllOperationsAreFinished一定要在操作隊列添加了操作后再設(shè)置掸掏。即茁影,先向operation queue中添加operation,再調(diào)用[operationQueue waitUntilAllOperationsAreFinished]丧凤。

NSBlockOperation *blkop = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"執(zhí)行操作 %@",[NSThread currentThread]);
    }];

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    [queue addOperation:blkop];
    // waitUntilAllOperationsAreFinished就像GCD的barrier一樣起到隔離作用
    // waitUntilAllOperationsAreFinished必須要在操作添加到隊列后設(shè)置
    // waitUntilAllOperationsAreFinished必須要在NSLog(@"finish");之前設(shè)置
waitUntilAllOperationsAreFinished
    [queue waitUntilAllOperationsAreFinished];

    NSLog(@"finish");
8 :GCD和nsoperation對比一下
<1>GCD

GCD是iOS4.0推出的募闲,主要針對多核cpu做了優(yōu)化,是C語言的技術(shù)

GCD是將任務(wù)(block)添加到隊列(串行/并行/全局/主隊列)愿待,并且以同步/異步的方式執(zhí)行任務(wù)的函數(shù),任務(wù)的取出遵循隊列的FIFO原則:先進(jìn)先出浩螺,后進(jìn)后出

GCD提供了一些NSOperation不具備的功能

一次性執(zhí)行:可以保證某一段代碼在程序運行的過程中只被執(zhí)行一次;一次性執(zhí)行是線程安全的,在多線程環(huán)境下也是只執(zhí)行一次;應(yīng)用場景:設(shè)計單例模式

延遲執(zhí)行:既實現(xiàn)等待多長時間后在哪個隊列中執(zhí)行什么代碼塊

調(diào)度組:監(jiān)聽一組異步任務(wù)執(zhí)行結(jié)束之后,我們能夠得到統(tǒng)一的通知;注意:在其調(diào)度組內(nèi)的任務(wù)執(zhí)行完畢后執(zhí)行后面的”刷新主界面”方法與”玩完”之間的執(zhí)行沒有先后順序;例如:

dispatch_group_notify(group,dispatch_get_main_queue(), ^{

//更新UI控件,提示用戶

NSLog(@"刷新主界面");

});

NSLog(@"玩完");
<2>NSOperation

NSOperation是iOS2.0推出的仍侥,iOS4.0之后重寫了NSOperation

NSOperation將操作(異步的任務(wù))添加到隊列(并發(fā)隊列)要出,就會執(zhí)行指定操作的方法

NSOperation里提供的方便的操作

最大并發(fā)數(shù),

隊列的暫定/繼續(xù)

取消隊列中所有的操作

指定操作之間的依賴關(guān)系(GCD可以用同步實現(xiàn),但是比較麻煩)

?同步和異步?jīng)Q定了要不要開啟新的線程(同步不開,異步開)

同步:在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力

異步:在新的線程中執(zhí)行任務(wù)农渊,具備開啟新線程的能力

?串行和并發(fā)決定了任務(wù)的執(zhí)行方式

并發(fā):多個任務(wù)并發(fā)(同時)執(zhí)行

串行:一個任務(wù)執(zhí)行完畢后患蹂,再執(zhí)行下一個任務(wù)

?當(dāng)任務(wù)是異步的時候,隊列決定了開啟多少條線程

串行隊列:只開一條

并發(fā)隊列:可以開啟多條

?主隊列特點:主隊列中的任務(wù),只有主線程空閑的時候才會調(diào)度任務(wù)執(zhí)行

主隊列又叫全局串行隊列,程序啟動的時候就創(chuàng)建了主隊列,在使用的時候不需要創(chuàng)建,直接GET.主隊列中的任務(wù)是要在主線程執(zhí)行的.

?主隊列,異步任務(wù)

不開線程,同步執(zhí)行

?主隊列传于,同步執(zhí)行

程序執(zhí)行不出來(死鎖)

死鎖的原因囱挑,當(dāng)程序執(zhí)行到下面這段代碼的時候

主隊列:如果主線程正在執(zhí)行代碼,就不調(diào)度任務(wù)

同步執(zhí)行:如果第一個任務(wù)沒有執(zhí)行格了,就繼續(xù)等待第一個任務(wù)執(zhí)行完成看铆,再執(zhí)行下一個任務(wù)此時互相等待,程序無法往下執(zhí)行(死鎖)

dispatch_sync(dispatch_get_main_queue(),^{

NSLog(@"%@ -- %d",[NSThreadcurrentThread],i);

});

?主隊列和串行隊列的區(qū)別

串行隊列:必須等待一個任務(wù)執(zhí)行完成盛末,再調(diào)度另一個任務(wù)

主隊列:以先進(jìn)先出調(diào)度任務(wù)弹惦,如果主線程上有代碼在執(zhí)行,主隊列不會調(diào)度任務(wù)

?(主隊列悄但,同步執(zhí)行)放入異步執(zhí)行解決死鎖

dispatch_async(dispatch_get_global_queue(0,0), ^{

NSLog(@"全局隊列棠隐,異步執(zhí)行%@",[NSThread currentThread]);

//此時這行代碼在子線程中運行,同步執(zhí)行不用等待主線程執(zhí)行此同步執(zhí)行的任務(wù)

dispatch_sync(dispatch_get_main_queue(), ^{

NSLog(@"主隊列,同步執(zhí)行%@",[NSThread currentThread]);

});

NSLog(@"==");

});

?全局隊列本質(zhì)就是并發(fā)隊列

dispatch_get_global_queue(0,0);

?全局隊列和并發(fā)隊列的區(qū)別

并發(fā)隊列有名稱檐嚣,可以跟蹤錯誤助泽,全局隊列沒有

全局隊列在ARC中不需要考慮釋放內(nèi)存,dispatch_release(q);不允許調(diào)用嚎京。并發(fā)隊列在MRC中需要手動釋放內(nèi)存嗡贺,因為并發(fā)隊列是create創(chuàng)建出來的在MRC中見到create就要release,全局隊列不需要release(只有一個)

一般我們使用全局隊列,因為使用起來更加簡單

<3>綜合比較其各自使用范圍如下

性能:①:GCD更接近底層鞍帝,而NSOperationQueue則更高級抽象诫睬,所以GCD在追求性能的底層操作來說,是速度最快的帕涌。這取決于使用Instruments進(jìn)行代碼性能分析摄凡,如有必要的話

②:從異步操作之間的事務(wù)性,順序行蚓曼,依賴關(guān)系亲澡。GCD需要自己寫更多的代碼來實現(xiàn),而NSOperationQueue已經(jīng)內(nèi)建了這些支持

③:如果異步操作的過程需要更多的被交互和UI呈現(xiàn)出來纫版,NSOperationQueue會是一個更好的選擇床绪。底層代碼中,任務(wù)之間不太互相依賴其弊,而需要更高的并發(fā)能力会涎,GCD則更有優(yōu)勢

歡迎更多開發(fā)者加qq 556120515 群進(jìn)來交流

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瑞凑,隨后出現(xiàn)的幾起案子末秃,更是在濱河造成了極大的恐慌,老刑警劉巖籽御,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件练慕,死亡現(xiàn)場離奇詭異惰匙,居然都是意外死亡,警方通過查閱死者的電腦和手機铃将,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門项鬼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人劲阎,你說我怎么就攤上這事绘盟。” “怎么了悯仙?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵龄毡,是天一觀的道長。 經(jīng)常有香客問我锡垄,道長沦零,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任货岭,我火速辦了婚禮路操,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘千贯。我一直安慰自己屯仗,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布搔谴。 她就那樣靜靜地躺著魁袜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪己沛。 梳的紋絲不亂的頭發(fā)上慌核,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天距境,我揣著相機與錄音申尼,去河邊找鬼。 笑死垫桂,一個胖子當(dāng)著我的面吹牛师幕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播诬滩,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼霹粥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了疼鸟?” 一聲冷哼從身側(cè)響起后控,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎空镜,沒想到半個月后浩淘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捌朴,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年张抄,在試婚紗的時候發(fā)現(xiàn)自己被綠了砂蔽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡署惯,死狀恐怖左驾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情极谊,我是刑警寧澤诡右,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站怀酷,受9級特大地震影響稻爬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蜕依,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一桅锄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧样眠,春花似錦友瘤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至被丧,卻和暖如春盟戏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背甥桂。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工柿究, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人黄选。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓蝇摸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親办陷。 傳聞我的和親對象是個殘疾皇子貌夕,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355