GCD

1.GCD

GCD(Grand Central Dispatch) 偉大的中樞調(diào)度器
GCD是蘋果為多核的并行運(yùn)算提出的方案

GCD會充分利用CPU內(nèi)核,會自動管理自己的生命周期

GCD 函數(shù)分為同步和異步

同步函數(shù)為 dispatch_sync
異步函數(shù)為 dispatch_async
1.1 核心概念

GCD 分為任務(wù),隊(duì)列

1.1.1.定制任務(wù)

1.1.2.將任務(wù)添加到隊(duì)列中(GCD會自動將隊(duì)列中的任務(wù)取出,放在對應(yīng)的線程來執(zhí)行)

1.2 隊(duì)列類型

1.并發(fā)和串行

1.1 并發(fā)是指多個任務(wù)同時進(jìn)行(自動開啟多個線程執(zhí)行)

1.2 并發(fā)功能只能在異步函數(shù)中進(jìn)行

2.串行隊(duì)列

讓任務(wù)一個接一個來執(zhí)行(上個任務(wù)完成后才接著進(jìn)行下一個)

2.容易混淆的術(shù)語
同步和異步 : 主要區(qū)別是,是否具有開線程的能力

并發(fā)和串行 : 這兩個是隊(duì)列,是執(zhí)行任務(wù)的方式

3 .GCD常用的幾種方法

3.1 異步并行(這種方式可以開啟線程,同時執(zhí)行多個任務(wù))

#pragma mark - 異步并行隊(duì)列
-(void)asyncConcurrent{

    // 創(chuàng)建隊(duì)列
    // 第一個參數(shù)是標(biāo)識,第二個是并行隊(duì)列
//    dispatch_queue_t queue = dispatch_queue_create("cnw", DISPATCH_QUEUE_CONCURRENT);
    
    // 創(chuàng)建全局隊(duì)列,第一個參數(shù)是隊(duì)列的優(yōu)先級,這個是獲得隊(duì)列,就已經(jīng)存在
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 封裝任務(wù),把任務(wù)放在隊(duì)列中執(zhí)行
    dispatch_async(queue, ^{
        NSLog(@"1-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });

}

3.2 異步串行(可以開啟線程,不過是開啟一條線程,任務(wù)有序執(zhí)行)

#pragma mark - 異步串行隊(duì)列
-(void)asyncSerial
{
    // 創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("cnw0", DISPATCH_QUEUE_SERIAL);
    
    // 封裝任務(wù)
    dispatch_async(queue, ^{
        NSLog(@"1-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });

}

3.3 同步串行(這種方法不會開啟線程,任務(wù)有序執(zhí)行)

#pragma mark - 同步串行隊(duì)列
-(void)syncSerial
{
    // 創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("cnw0", DISPATCH_QUEUE_SERIAL);
    
    // 封裝任務(wù)
    dispatch_sync(queue, ^{
        NSLog(@"1-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    
}

3.4 同步并行(不會開啟線程,任務(wù)有序執(zhí)行)

#pragma mark - 同步并行隊(duì)列
-(void)syncConcurrent
{
    // 創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("cnw0", DISPATCH_QUEUE_CONCURRENT);
    
    // 封裝任務(wù)
    dispatch_sync(queue, ^{
        NSLog(@"1-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    
}

4.主隊(duì)列的特殊性

當(dāng)前隊(duì)列為主隊(duì)列時,異步執(zhí)行不會創(chuàng)建線程.
有主隊(duì)列時,執(zhí)行方法是在主線程中執(zhí)行的,不會在子線程中執(zhí)行

#pragma mark - 異步主隊(duì)列
-(void)asyncMain
{
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 封裝任務(wù),把任務(wù)放在隊(duì)列中執(zhí)行
    dispatch_async(queue, ^{
        NSLog(@"1-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    
}

打印結(jié)果

2017-03-24 15:02:48.962 GCD[10228:591925] 1-------當(dāng)前隊(duì)列<NSThread: 0x60800006e100>{number = 1, name = main}
2017-03-24 15:02:48.962 GCD[10228:591925] 2-------當(dāng)前隊(duì)列<NSThread: 0x60800006e100>{number = 1, name = main}
2017-03-24 15:02:48.962 GCD[10228:591925] 3-------當(dāng)前隊(duì)列<NSThread: 0x60800006e100>{number = 1, name = main}

當(dāng)在同步函數(shù)中執(zhí)行主隊(duì)列時會出現(xiàn)死鎖

#pragma mark - 同步主隊(duì)列
-(void)syncMain
{
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    
    NSLog(@"開始執(zhí)行");
    // 封裝任務(wù),把任務(wù)放在隊(duì)列中執(zhí)行
    dispatch_sync(queue, ^{
        NSLog(@"1-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-------當(dāng)前隊(duì)列%@",[NSThread currentThread]);
    });
    NSLog(@"完成執(zhí)行");
}

原因是當(dāng)執(zhí)行主隊(duì)列的任務(wù)時,會在主線程執(zhí)行,而當(dāng)前是同步函數(shù),任務(wù)是一個完成后接著下一個,所以當(dāng)進(jìn)行第一個線程的時候,會在主線程執(zhí)行,而此時主線程在等這個任務(wù)完成.

主隊(duì)列特點(diǎn):如果主隊(duì)列發(fā)現(xiàn)當(dāng)前主線程有任務(wù)在執(zhí)行,那么主隊(duì)列會暫停調(diào)用隊(duì)列中的任務(wù),知道主線程空閑為止.

如果在子線程中執(zhí)行同步函數(shù)則可以有序執(zhí)行任務(wù)
最后附上一張表

Snip.png
  1. GCD線程間的通信
    還是下載圖片,當(dāng)下載完圖片以后,回到主線程,只需要是主隊(duì)列就會在主線程執(zhí)行

這時候同步回到主線程是不會出現(xiàn)死鎖的,因?yàn)檫@一步操作是在子線程中操作的

#pragma mark - GCD線程間通信
-(void)threadMes
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        UIImage * image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1490352377321&di=57b814f647287d559e264311887c5e9c&imgtype=0&src=http%3A%2F%2Fimg.sj33.cn%2Fuploads%2Fallimg%2F201402%2F102J0KJ-24.jpg"]]];
        
        // 加載好數(shù)據(jù)以后,在UI界面顯示回到主線程
        //        dispatch_sync(dispatch_get_main_queue()
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
            NSLog(@"當(dāng)前線程是00%@",[NSThread currentThread]);
        });
        
        NSLog(@"當(dāng)前線程是01%@",[NSThread currentThread]);
    });
    

}

6.GCD的兩個函數(shù)

6.1 延時操作

#pragma mark - GCD延時操作
-(void)delay
{
    NSLog(@"開始任務(wù)");
    // 第一種方法
//    [self performSelector:@selector(run) withObject:self afterDelay:2.0];
    // 第二種方法
//    [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 第三種方法GCD
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
        // 可以在主線程,也可以現(xiàn)在子線程
         NSLog(@"%@",[NSThread currentThread]);
        
    });

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

6.2 只會創(chuàng)建一次(創(chuàng)建單例時會用到)

#pragma mark - GCD只會創(chuàng)建一次的操作
-(void)once
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"打印了幾次");
    });

}

7.柵欄函數(shù)
根據(jù)表面意思就知道,柵欄:攔截的意思,就是等上邊的任務(wù)執(zhí)行完成以后,才可以往下進(jìn)行

但柵欄函數(shù)的使用隊(duì)列,不能是全局隊(duì)列,不然是沒效果的

#pragma mark - 柵欄函數(shù)
-(void)railings
{
     dispatch_queue_t queue = dispatch_queue_create("cnw01", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"thread1--------%@",[NSThread currentThread]);
    });
    
   
    
    dispatch_async(queue, ^{
        NSLog(@"thread3--------%@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        
        NSLog(@"thread2??--------%@",[NSThread currentThread]);
    });   
}

8 . GCD的快速迭代(剪切文件)

#pragma mark - 快速迭代
-(void)apply
{
    // 首先獲得需要剪切的文件夾
    NSString * frome = @"/Users/chenningwei/Desktop/屏幕截圖";
    // 剪切到的文件夾
    NSString * to = @"/Users/chenningwei/Desktop/目標(biāo)文件";
    // 得到需要剪切文件夾內(nèi)的元素
    NSArray * files =  [[NSFileManager defaultManager] subpathsAtPath:frome];
    
    // 遍歷數(shù)組中的元素
    NSInteger count = files.count;
    dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index) {
        
        // 獲得剪切文件內(nèi)文件的全路徑
        NSString * fromePath = [frome stringByAppendingPathComponent:files[index]];
        // 剪切到文件夾文件全路徑
        NSString * toPath = [to stringByAppendingPathComponent:files[index]];
        // 剪切
        [[NSFileManager defaultManager] moveItemAtPath:fromePath toPath:toPath error:nil];
        NSLog(@"%@----%@-----%@",fromePath,toPath,[NSThread currentThread]);
    });
    

}

9 . 隊(duì)列組
隊(duì)列組執(zhí)行是可以等上邊操作完成以后,來進(jìn)行下一步的操作,這是一般的異步操作實(shí)現(xiàn)不了了(目前我的水平)
比如:下載兩張圖片后,進(jìn)行合成
只有兩張圖片下載完成以后才可以進(jìn)行合并,而普通的異步操作是無序的實(shí)現(xiàn)不了,就需要用到隊(duì)列組來實(shí)現(xiàn),下面看代碼

#pragma mark - 隊(duì)列組下載圖片
-(void)queues
{
    // 創(chuàng)建隊(duì)列組
    dispatch_group_t group = dispatch_group_create();
    // 全局隊(duì)列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 下載第一張圖片
    dispatch_group_async(group, queue, ^{
        NSURL * url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1490352377321&di=57b814f647287d559e264311887c5e9c&imgtype=0&src=http%3A%2F%2Fimg.sj33.cn%2Fuploads%2Fallimg%2F201402%2F102J0KJ-24.jpg"];
        NSData * data = [NSData dataWithContentsOfURL:url];
        self.image1= [UIImage imageWithData:data];
        
    });
    dispatch_group_async(group, queue, ^{
        NSURL * url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1490352377321&di=57b814f647287d559e264311887c5e9c&imgtype=0&src=http%3A%2F%2Fimg.sj33.cn%2Fuploads%2Fallimg%2F201402%2F102J0KJ-24.jpg"];
        NSData * data = [NSData dataWithContentsOfURL:url];
        self.image2= [UIImage imageWithData:data];
        
    });
    
    // 等兩張圖片下載完以后進(jìn)行合成
    dispatch_group_notify(group, queue, ^{
        
        // 開始圖形上下文
        UIGraphicsBeginImageContext(CGSizeMake(200, 200));
        
        //畫圖
        [self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
        self.image1 = nil;
        [self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
        self.image2 = nil;
        // 根據(jù)圖形上下文得到一張圖片
        UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
        // 關(guān)閉圖形上下文
        UIGraphicsEndPDFContext();
        
        // 設(shè)置圖片
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView1.image = image;
        });
        
        
    });

    
}

10 . ARC環(huán)境下的單例模式

 .1 首先定義一個全局變量
    static Tool * _instance;
 .2 重寫allocWithZone方法
+(instancetype)allocWithZone:(struct _NSZone *)zone
在這方法里創(chuàng)建單例
 
// 第一種懶加載方法
// 防止多條線程共享一塊資源出現(xiàn)的安全問題,加互斥鎖
@synchronized(self){
   if ( _instance == nil ){
      _instance = [super  allocWithZone:zone];
      }
}
   return  _instance;
// 第二種利用GCD創(chuàng)建
 static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    
    return _instance;
// 在.h文件中可以定義個類方法,供外界調(diào)用,在.m文件中的實(shí)現(xiàn)方法
+(instancetype)shareTool
{
    return [[self alloc] init];
}

// 但是為了安全起見,會重寫
-(id)copyWithZone:(NSZone *)zone
{
    return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
    return _instance;
}

最后編輯于
?著作權(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)容

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