iOS 多線程詳解

在了解GCD之前,我們首先要知道幾個(gè)概念侠鳄。關(guān)于隊(duì)列和同/異步函數(shù)愚屁。為了讓讀者更簡(jiǎn)單直觀的理解這些概念,我盡可能用最簡(jiǎn)單的話進(jìn)行解釋絮重。

關(guān)于隊(duì)列

主隊(duì)列:專門在主線程上調(diào)度任務(wù)冤寿。
串行隊(duì)列:顧名思義,也就是任務(wù)一個(gè)接著一個(gè)的執(zhí)行(按順序執(zhí)行)青伤。
并發(fā)隊(duì)列:顧名思義督怜,任務(wù)可以同時(shí)執(zhí)行。
全局隊(duì)列:本質(zhì)上還是并發(fā)隊(duì)列狠角。

關(guān)于同/異步函數(shù)

同步函數(shù):不會(huì)開啟新線程号杠。在主線程馬上執(zhí)行任務(wù)。
異步函數(shù):會(huì)開啟新線程丰歌。不會(huì)馬上執(zhí)行任務(wù)姨蟋。

隊(duì)列和同/異步函數(shù)聯(lián)合使用

主隊(duì)列+同步函數(shù)

上面說過,開啟同步任務(wù)不會(huì)開啟新線程立帖,而是在主線程馬上執(zhí)行任務(wù)眼溶。但是主線程有正在執(zhí)行的任務(wù),只有主線程的任務(wù)執(zhí)行完畢晓勇,才能執(zhí)行主隊(duì)列中新加的任務(wù)堂飞。而主隊(duì)列也在等待主線程的任務(wù)執(zhí)行完畢灌旧,然后執(zhí)行自己的任務(wù)。

主隊(duì)列+異步函數(shù)

上面說過酝静,異步函數(shù)不會(huì)馬上執(zhí)行任務(wù)节榜,并且會(huì)開啟新線程。但是因?yàn)槭窃谥麝?duì)列中使用異步函數(shù)(因?yàn)橹麝?duì)列是在主線程上調(diào)度任務(wù)),所以既不會(huì)馬上執(zhí)行任務(wù)畸悬,也不會(huì)開啟新線程汗侵。而是等主線程的任務(wù)全部執(zhí)行完畢后,等主線程有空了再去執(zhí)行異步任務(wù)锰悼。看看代碼效果。

-(void)test {
    NSLog(@"touchesBegan");
    // 主隊(duì)列+異步函數(shù)
    // 等主線程任務(wù)執(zhí)行完畢之后,再執(zhí)行 test2
    dispatch_async(dispatch_get_main_queue(), ^{
          [self test2];
    });
  
    [self test3];     
    NSLog(@"touchesEnd");
}

- (void)test2 {
    NSLog(@"test2-----------%@",[NSThread currentThread]);
}

- (void)test3 {
    NSLog(@"test3-----------%@",[NSThread currentThread]);
}

控制臺(tái)輸出:

output.png

number = 1, name = main,證明任務(wù)是在當(dāng)前線程(主線程)執(zhí)行的敞恋。
使用場(chǎng)景:
1.回到主線程更新UI
2.調(diào)整任務(wù)執(zhí)行順序

同步函數(shù)+其他隊(duì)列

串行隊(duì)列:會(huì)在當(dāng)前線程同步執(zhí)行。
并發(fā)隊(duì)列:會(huì)在當(dāng)前線程同步執(zhí)行谋右,這種使用方式?jīng)]有意義硬猫,不推薦。

異步函數(shù)+其他隊(duì)列

串行隊(duì)列:因?yàn)榇嘘?duì)列是一個(gè)執(zhí)行完后再執(zhí)行下一個(gè)改执,所以只會(huì)多開一條新線程調(diào)度任務(wù)啸蜜。
并發(fā)隊(duì)列:因?yàn)椴l(fā)隊(duì)列是多個(gè)任務(wù)同時(shí)執(zhí)行,所以會(huì)開多條新線程調(diào)度任務(wù)辈挂。

GCD

線程間通信

例:開啟一條子線程進(jìn)行下載圖片的耗時(shí)操作衬横,圖片下載完后回到主線程更新UI。

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 1. 開啟子線程下載圖片
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 下載圖片!
        UIImage *image = [self downloadWebImage];
        // 回到主線程顯示圖片!
        dispatch_async(dispatch_get_main_queue(), ^{
             NSLog(@"顯示圖片:%@",[NSThread currentThread]);
            // 顯示圖片!
            // 如果設(shè)置按鈕的圖片,必須改變按鈕的類型為 custom!
            //[self.button setImage:image forState:UIControlStateNormal];
            // 設(shè)置按鈕的背景圖片: 
           [self.button setBackgroundImage:image forState:UIControlStateNormal];
        });
    });
 }

// 下載網(wǎng)絡(luò)圖片
// 返回值: 下載好的圖片!
-(UIImage *)downloadWebImage{
    NSLog(@"downloadWebImage:%@",[NSThread currentThread]);
    NSString *urlString = @"http://d.hiphotos.baidu.com/image/pic/item/64380cd7912397dd2a7d71d15d82b2b7d1a287db.jpg";
    NSURL *url = [NSURL URLWithString:urlString];
    // 這是一個(gè)耗時(shí)方法!這一個(gè)方法內(nèi)部封裝了很多代碼(關(guān)于網(wǎng)絡(luò)的代碼!)!
    NSData *data = [NSData dataWithContentsOfURL:url];
    // 下載好的圖片
    UIImage *image = [UIImage imageWithData:data];
    return image;
}
調(diào)度組

關(guān)鍵字: dispatch_group_t
作用:管理隊(duì)列的!
(1)參數(shù)說明
參數(shù)1:標(biāo)識(shí)隊(duì)列組!
參數(shù)2:管理的隊(duì)列!
參數(shù)3:添加到隊(duì)列中的任務(wù)!

dispatch_group_async(<#dispatch_group_t group#>, <#dispatch_queue_t queue#>, <#^(void)block#>)

(2)關(guān)鍵函數(shù)
等待隊(duì)列組中的任務(wù)都執(zhí)行完畢之后,就會(huì)發(fā)出通知,調(diào)用下面的方法!
隊(duì)列組: 等待哪一個(gè)隊(duì)列組中的任務(wù)執(zhí)行完畢!
隊(duì)列: 決定后續(xù)的任務(wù)在哪條線程執(zhí)行
任務(wù): 后續(xù)的任務(wù)!

dispatch_group_notify(<#dispatch_group_t group#>, <#dispatch_queue_t queue#>, <#^(void)block#>)

需求:下載兩張圖片终蒂,下載完成后蜂林,合并兩張圖片并顯示

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    // 創(chuàng)建一個(gè)隊(duì)列組!
    dispatch_group_t group = dispatch_group_create();

    //變量聲明
    __block UIImage *image1 ,*image2;

    // 下載第一張圖片
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{       
        image1 = [self downloadImageWithUrlString:@"http://g.hiphotos.baidu.com/image/pic/item/95eef01f3a292df54e0e7e08be315c6035a873da.jpg"];
    });

    // 下載第二張圖片
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{       
        image2 = [self downloadImageWithUrlString:@"http://e.hiphotos.baidu.com/image/pic/item/cc11728b4710b912d4bb69ffc1fdfc03924522bc.jpg"];
    });

    // 合并圖片并且顯示
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 合并圖片
        UIImage *image = [self bingImageWithImage1:image1 Image2:image2];
        // 顯示合并之后的圖片!
        self.imageView.image = image;       
    });
}

// 合并圖片
-(UIImage *)bingImageWithImage1:(UIImage *)image1 Image2:(UIImage *)image2{
    // 1.開啟圖形上下文
    UIGraphicsBeginImageContext(self.imageView.bounds.size);
    //2.繪制第一張圖片
    [image1 drawInRect:self.imageView.bounds];
    // 3.繪制第二張圖片
    [image2 drawInRect:CGRectMake(0, self.imageView.bounds.size.height - 80, self.imageView.bounds.size.width, 80)];
    // 4.獲取繪制好的圖片
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    // 5.關(guān)閉圖形上下文
    UIGraphicsEndImageContext();

    return image;
}

// 下載圖片
-(UIImage *)downloadImageWithUrlString:(NSString *)urlString{
    NSURL *url = [NSURL URLWithString:urlString];
    NSData *data = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:data];
    return image;
}
一次性代碼

使用場(chǎng)景:?jiǎn)卫K_保代碼只被執(zhí)行一次拇泣,不管有多少個(gè)線程噪叙。GCD的dispatch_once可以確保代碼只被執(zhí)行一次。

@interface Person : NSObject
+ (instancetype)sharedPerson;
@end

@implementation Person

+ (instancetype)sharedPerson{
    static id instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    }); 
    return instance;
}
@end
阻塞式函數(shù)

使用場(chǎng)景:任務(wù)1和任務(wù)2同時(shí)執(zhí)行霉翔,然后執(zhí)行任務(wù)3睁蕾,然后任務(wù)4和任務(wù)5同時(shí)執(zhí)行。
需要注意:阻塞式函數(shù)只有自己創(chuàng)建的并發(fā)隊(duì)列才有用早龟,對(duì)全局并發(fā)隊(duì)列無效惫霸。
關(guān)鍵字: dispatch_barrier_async

- (void)test {
    // 需求:任務(wù) 1~5 ,執(zhí)行順序:
    // 第三個(gè)任務(wù)必須等待前兩個(gè)任務(wù)執(zhí)行完畢之后,再執(zhí)行!
    // 后兩個(gè)任務(wù)必須等待第三個(gè)任務(wù)完成之后再執(zhí)行!
    // 任務(wù)1和2 同時(shí)執(zhí)行, 任務(wù)4和5同時(shí)執(zhí)行!
    // 阻塞式函數(shù)! -- 主要是阻塞并發(fā)隊(duì)列的!(只有自己創(chuàng)建的并發(fā)隊(duì)列才有效,全局并發(fā)隊(duì)列不可以使用阻塞式函數(shù)).
    // 1.隊(duì)列:需要阻塞哪一個(gè)隊(duì)列
    // 2.任務(wù):阻塞隊(duì)列的任務(wù)! 任務(wù)3/阻塞式任務(wù)!
    // dispatch_barrier_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
    // 創(chuàng)建并發(fā)隊(duì)列,按順序往并發(fā)隊(duì)列中添加任務(wù)!
    // 創(chuàng)建一個(gè)并發(fā)隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);

    // 按順序往并發(fā)隊(duì)列中添加任務(wù)
    dispatch_async(queue, ^{
         //模擬耗時(shí)操作
         [NSThread sleepForTimeInterval:5];
         NSLog(@"任務(wù)1:%@",[NSThread currentThread]);
    });

    dispatch_async(queue, ^{
         //模擬耗時(shí)操作
         [NSThread sleepForTimeInterval:3];
         NSLog(@"任務(wù)2:%@",[NSThread currentThread]);
    });

    // 任務(wù)3是一個(gè)阻塞式任務(wù),利用阻塞式函數(shù)添加
    // 同步和異步都是相對(duì)于當(dāng)前線程來說的!
    dispatch_barrier_sync(queue, ^{
        //模擬耗時(shí)操作
        [NSThread sleepForTimeInterval:5];
        NSLog(@"任務(wù)3:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:5];
    });

    dispatch_async(queue, ^{
         NSLog(@"任務(wù)4:%@",[NSThread currentThread]);
    });

    dispatch_async(queue, ^{
         NSLog(@"任務(wù)5:%@",[NSThread currentThread]);
    });
}
延時(shí)執(zhí)行

這里不止有GCD的延時(shí)執(zhí)行方法。在了解這兩個(gè)延時(shí)執(zhí)行方法之前我們還是要了解一些和Runloop有關(guān)的概念性知識(shí)葱弟,因?yàn)檫@很有必要壹店。當(dāng)然只是做一些簡(jiǎn)單的介紹,深入的介紹會(huì)在以后陸續(xù)講解芝加。

1硅卢、主線程和子線程的區(qū)別
主線程的運(yùn)行循環(huán)默認(rèn)是開啟的,子線程的運(yùn)行循環(huán)默認(rèn)是關(guān)閉的!

2射窒、Runloop
運(yùn)行循環(huán)驅(qū)動(dòng)(執(zhí)行)事件源(UI操作/點(diǎn)擊/滾動(dòng)/定時(shí)器/特殊的事件)。

運(yùn)行循環(huán)是一個(gè)死循環(huán)(do...while...循環(huán))!平時(shí)沒有事件驅(qū)動(dòng)的時(shí)候,運(yùn)行循環(huán)就處于睡眠狀態(tài)!當(dāng)有一些事件源發(fā)生的時(shí)候,就會(huì)喚醒運(yùn)行循環(huán)來執(zhí)行事件!事件執(zhí)行完畢之后,運(yùn)行循環(huán)接著進(jìn)入睡眠狀態(tài)!

運(yùn)行循環(huán)的開啟需要事件源的驅(qū)動(dòng)!一個(gè)線程中,如果沒有事件源,運(yùn)行循環(huán)無法開啟!

3将塑、兩種延遲執(zhí)行的方式
(1) GCD

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        <#code to be executed after a specified delay#>
});

(2) other

self performSelector:<#(nonnull SEL)#> withObject:<#(nullable id)#> afterDelay:<#(NSTimeInterval)#>
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    NSLog(@"touchesBegan");
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       
        [self performSelector:@selector(test) withObject:nil afterDelay:3.0];
        
        NSLog(@"能夠到這里嗎?");
    });
    
    NSLog(@"touchesEnd");
}

- (void)test {
    NSLog(@"test");
}

輸出結(jié)果:


Snip20160907_432.png

通過控制臺(tái)輸出可以看到test方法的Log并沒有打印脉顿。
為什么呢?
方式(2)是一個(gè)事件源点寥,一個(gè)特殊的事件源艾疟,這句代碼執(zhí)行完后,會(huì)自動(dòng)關(guān)閉運(yùn)行循環(huán)敢辩。
需要延遲執(zhí)行的test方法并沒有執(zhí)行,因?yàn)檫@句代碼執(zhí)行完后就關(guān)閉了運(yùn)行循環(huán)蔽莱。
所以為了延遲執(zhí)行test方法,需要在執(zhí)行完這句代碼后開啟運(yùn)行循環(huán)戚长,代碼 [[NSRunLoop currentRunLoop] run];
[[NSRunLoop currentRunLoop] run]最好寫成 CFRunLoopRun();這樣寫方便我們關(guān)閉它盗冷。
因?yàn)镹SRunLoop并沒有為我們提供關(guān)閉它的方法,而 CFRunLoopRun()給我們提供了關(guān)閉運(yùn)行循環(huán)的方法,如下

CFRunLoopGetCurrent():獲得當(dāng)前線程的運(yùn)行循環(huán)!
CFRunLoopStop(CFRunLoopGetCurrent()); 關(guān)閉運(yùn)行循環(huán)!

所以還要注意同廉,如果要在一個(gè)子線程中開啟一個(gè)定時(shí)器仪糖,把當(dāng)前定時(shí)器添加到運(yùn)行循環(huán)后,一定要開啟運(yùn)行循環(huán)迫肖。(因?yàn)樽泳€程的運(yùn)行循環(huán)默認(rèn)是關(guān)閉的)锅劝。

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    NSLog(@"touchesBegan");
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       
        //創(chuàng)建一個(gè)定時(shí)器
        NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
        
        //把定時(shí)器加入到當(dāng)前的運(yùn)行循環(huán)
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        
        //開啟運(yùn)行循環(huán)
        CFRunLoopRun();
        
        NSLog(@"come here! %@",[NSThread currentThread]);
        
    });
    
    NSLog(@"touchesEnd");
}

- (void)test {
    static int i = 0;
    
    NSLog(@" i = %d",i);
    
    if (i == 6) {
        CFRunLoopStop(CFRunLoopGetCurrent());
        NSLog(@"關(guān)閉運(yùn)行循環(huán)!");
    }
}

控制臺(tái)輸出:

output.png

NSOperation & NSOperationQueue

NSOperation是對(duì)GCD的封裝,面向操作編程咒程。

NSOperation有兩種方式創(chuàng)建操作對(duì)象

(1)

NSInvocationOperation *opInvo = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];

(2)

NSBlockOperation *opBlock = [NSBlockOperation blockOperationWithBlock:^{
        //xxxx
}];
NSOperation的執(zhí)行

第一種情況:
將操作添加到隊(duì)列中
[queue addOperation:op];
使用這種方式向隊(duì)列中添加操作鸠天,所有操作都會(huì)在子線程中執(zhí)行。
所以帐姻,建議如果NSBlockOperation需要在主線程執(zhí)行稠集,就不要追加操作。

第二種情況:
調(diào)用start方法
[op start];
NSBlockOperation 如果追加了任務(wù)! 直接調(diào)用 start 方法或者將操作添加到主隊(duì)列, 這個(gè)時(shí)候,操作中的任務(wù)會(huì)在不同的線程執(zhí)行(主線程 + 子線程)饥瓷。

兩種隊(duì)列

主隊(duì)列:在主線程中完成操作
非主隊(duì)列:在子線程中完成操作
(1)創(chuàng)建主隊(duì)列

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
// 添加操作到隊(duì)列中!
// 將操作添加到隊(duì)列中之后,就會(huì)自動(dòng)執(zhí)行 NSOperation 對(duì)象的 main 方法!
[queue1 addOperation:op];

(2)創(chuàng)建非主隊(duì)列

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 添加操作到隊(duì)列中!
// 將操作添加到隊(duì)列中之后,就會(huì)自動(dòng)執(zhí)行 NSOperation 對(duì)象的 main 方法!
[queue addOperation:op];

(3)往隊(duì)列中添加操作的簡(jiǎn)便寫法

// 缺點(diǎn): 沒有一個(gè)操作對(duì)象.不能對(duì)操作做后續(xù)的管理!
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
     NSLog(@"任務(wù):%@",[NSThread currentThread]);
}];
NSBlockOperation追加任務(wù)及需要注意的點(diǎn)
  1. 創(chuàng)建一個(gè)操作
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任務(wù)1 %@",[NSThread currentThread]);
}];

2.NSBlockOperation 可以追加任務(wù)!

    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op1 - %@",[NSThread currentThread]);
    }];
    
    [op addExecutionBlock:^{
        NSLog(@"任務(wù)2 %@",[NSThread currentThread]);
    }];
    
    [op addExecutionBlock:^{
        NSLog(@"任務(wù)3 %@",[NSThread currentThread]);
    }];
    
    [op addExecutionBlock:^{
        NSLog(@"任務(wù)4 %@",[NSThread currentThread]);
    }];
    
    [op start];

控制臺(tái)輸出:


Snip20160907_434.png

也就是說追加的任務(wù)全部是在子線程中執(zhí)行的剥纷!

NSOperation的高級(jí)使用方法

操作依賴

比如有如下需求:
有5個(gè)操作1-5,操作1-3在子線程中執(zhí)行呢铆,操作4-5在主線程中執(zhí)行晦鞋,并且操作的執(zhí)行順序是1,2棺克,3悠垛,4,5娜谊。這個(gè)時(shí)候就需要使用操作依賴确买。讓后一個(gè)操作依賴前一個(gè)操作,并且操作依賴可以夸隊(duì)列(線程)纱皆。

NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任務(wù)1 %@",[NSThread currentThread]);
}];

NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任務(wù)2 %@",[NSThread currentThread]);
}];

NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{       
    NSLog(@"任務(wù)3 %@",[NSThread currentThread]);
}];

NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任務(wù)4 %@",[NSThread currentThread]);
}];

NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任務(wù)5 %@",[NSThread currentThread]);
}];
// 創(chuàng)建非主隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
// 保證操作按順序執(zhí)行 --- 添加操作依賴!       
// 能夠保證先執(zhí)行 op2 ,再執(zhí)行 op1    
// 操作依賴內(nèi)部使用了:線程同步技術(shù)!
// 添加操作依賴注意:
// 注意1: 不要添加循環(huán)依賴!
// 注意2: 一定要先添加操作依賴,然后再把操作添加到操作隊(duì)列中!    
// 注意3: 對(duì)不不同隊(duì)列中的操作,添加操作依賴依然有效!    
[op2 addDependency:op1];    
[op3 addDependency:op2];    
[op4 addDependency:op3];
[op5 addDependency:op4];
[queue addOperation:op1];    
[queue addOperation:op2];    
[queue addOperation:op3];

NSOperationQueue *queue2 = [NSOperationQueue mainQueue];
[queue2 addOperation:op4];
[queue2 addOperation:op5];

控制臺(tái)的輸出一定是按照順序輸出的!

隊(duì)列操作

操作隊(duì)列可以管理操作湾趾,可以 暫停/恢復(fù)/取消操作芭商, 實(shí)際開發(fā)中,為了在任何時(shí)候都能夠使用這個(gè)隊(duì)列,做成全局的!

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 暫停隊(duì)列中的操作
    [queue setSuspended:YES];
    // 恢復(fù)隊(duì)列中的操作
    [queue setSuspended:NO];
    // 取消隊(duì)列中的所有操作
    // 取消操作:對(duì)于已經(jīng)開始的操作是無法取消的!
    // 系統(tǒng)接收到內(nèi)存警告的時(shí)候!
    [queue cancelAllOperations];
    // 取消單個(gè)操作!
    [op cancel];
隊(duì)列間線程通信
     NSOperationQueue *queue = [[NSOperationQueue alloc] init];
       [queue addOperationWithBlock:^{
        // 1.異步下載圖片
        NSURL *url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/pic/item/37d3d539b6003af3290eaf5d362ac65c1038b652.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:data];
        // 2.回到主線程,顯示圖片
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
             self.imageView.image = image;
        }];
    }];

如何選擇使用GCD還是NSOperation ?

首先搀缠,NSOperation是對(duì)GCD的封裝铛楣,在使用上當(dāng)然是GCD更具有效率。
NSOperation是面向操作編程艺普,GCD是面向任務(wù)編程簸州。
使用NSOperation我們可以將某個(gè)操作暫停,恢復(fù)或取消操作衷敌∥鸷睿可以滿足許多使用場(chǎng)景,比如一個(gè)tableView上下滾動(dòng)的時(shí)候缴罗,如果開始滾動(dòng)就暫停下載操作,如果停止?jié)L動(dòng)就恢復(fù)下載操作祭埂。如果系統(tǒng)提示內(nèi)存警告還可以取消操作面氓。
但是GCD就不具備NSOperation的上述功能,GCD適用一些簡(jiǎn)單的需求,比如簡(jiǎn)單的多線程操作等等蛆橡。
所以根據(jù)具體需求選擇相應(yīng)的實(shí)現(xiàn)技術(shù)舌界。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市泰演,隨后出現(xiàn)的幾起案子呻拌,更是在濱河造成了極大的恐慌,老刑警劉巖睦焕,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件藐握,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡垃喊,警方通過查閱死者的電腦和手機(jī)猾普,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來本谜,“玉大人初家,你說我怎么就攤上這事∥谥” “怎么了溜在?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)他托。 經(jīng)常有香客問我掖肋,道長(zhǎng),這世上最難降的妖魔是什么上祈? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任培遵,我火速辦了婚禮浙芙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘籽腕。我一直安慰自己嗡呼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布皇耗。 她就那樣靜靜地躺著南窗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪郎楼。 梳的紋絲不亂的頭發(fā)上万伤,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音呜袁,去河邊找鬼敌买。 笑死,一個(gè)胖子當(dāng)著我的面吹牛阶界,可吹牛的內(nèi)容都是我干的虹钮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼膘融,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼芙粱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起氧映,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤春畔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后岛都,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體律姨,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年疗绣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了线召。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡多矮,死狀恐怖缓淹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情塔逃,我是刑警寧澤讯壶,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站湾盗,受9級(jí)特大地震影響伏蚊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜格粪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一躏吊、第九天 我趴在偏房一處隱蔽的房頂上張望氛改。 院中可真熱鬧,春花似錦比伏、人聲如沸胜卤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽葛躏。三九已至,卻和暖如春悠菜,著一層夾襖步出監(jiān)牢的瞬間舰攒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工悔醋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留摩窃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓篙顺,卻偏偏與公主長(zhǎng)得像偶芍,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子德玫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 歡迎大家指出文章中需要改正或者需要補(bǔ)充的地方,我會(huì)及時(shí)更新椎麦,非常感謝宰僧。 一. 多線程基礎(chǔ) 1. 進(jìn)程 進(jìn)程是指在系...
    xx_cc閱讀 7,188評(píng)論 11 70
  • 1、簡(jiǎn)介 NSOperation是蘋果提供給我們的一套多線程解決方案观挎。實(shí)際上NSOperation是基于GCD更高...
    WQ_UESTC閱讀 958評(píng)論 0 6
  • Object C中創(chuàng)建線程的方法是什么琴儿?如果在主線程中執(zhí)行代碼,方法是什么嘁捷?如果想延時(shí)執(zhí)行代碼造成、方法又是什么? 1...
    AlanGe閱讀 1,736評(píng)論 0 17
  • 什么是進(jìn)程雄嚣? 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序晒屎。 每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存...
    珍此良辰閱讀 1,255評(píng)論 1 5
  • 我羨慕那些能夠輕輕松松的就把自己委屈跟不開心的講出來缓升,我羨慕那些家庭美滿不知世間丑態(tài)的天真小孩鼓鲁,我羨慕那些拿不起又...
    啊曼花閱讀 571評(píng)論 4 6