iOS開發多線程篇—GCD的常見用法

1、延遲執行

    1.介紹

    iOS常見的延遲執行有2種方式網絡

        (1)調用NSObject的方法        併發

         [self performSelector"@selector(run) withObject:nil afterDelay:2.0];異步

         //2秒後再調用self的run 方法async

        (2)GCD函數
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int16_t)(2.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                //2秒後異步執行這裏的代碼
            });函數

    2.說明
    第一種方法,該方法在那個線程調用,那麼run就在哪一個線程執行(當前線程),一般是主線程。
 url

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"打印線程----%@",[NSThread currentThread]);
    //延遲執行
    //第一種方法:延遲3秒鐘調用run函數
    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
    
}
-(void)run
{
    NSLog(@"延遲執行----%@",[NSThread currentThread]);
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //在異步函數中執行
    dispatch_queue_t queue = dispatch_queue_create("wendingding", 0);
    
    dispatch_sync(queue, ^{
        [self performSelector:@selector(test) withObject:nil afterDelay:1.0];
    });
    NSLog(@"異步函數");
}
-(void)test
{
    NSLog(@"異步函數中延遲執行----%@",[NSThread currentThread]);
}
@end
spa

    說明:若是把該方法放在異步函數中執行,則方法不會被調用(BUG?)線程

    

    第二種方法3d

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"打印當前線程---%@",  [NSThread currentThread]);
    
    //延遲執行,第二種方式
     //能夠安排其線程(1),主隊列
     dispatch_queue_t queue= dispatch_get_main_queue();
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
        NSLog(@"主隊列--延遲執行------%@",[NSThread currentThread]);
    });
    
    //能夠安排其線程(2),併發隊列
    //1.獲取全局併發隊列
    dispatch_queue_t queue1= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //2.計算任務執行的時間
    dispatch_time_t when=dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
    //3.會在when這個時間點,執行queue中的這個任務
    dispatch_after(when, queue1, ^{
        NSLog(@"併發隊列-延遲執行------%@",[NSThread currentThread]);
    });
}
@end

         

    延遲執行:不須要再寫方法,且它還傳遞了一個隊列,咱們能夠指定並安排其線程。  code

    若是隊列是主隊列,那麼就在主線程執行,若是隊列是併發隊列,那麼會新開啓一個線程,在子線程中執行。


2、一次性代碼

//-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
//{
//    if (_log==NO) {
//        NSLog(@"該行代碼只執行一次");
//        _log=YES;
//    }
//}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"該行代碼只執行一次");
    });
}

    1.註釋中方法缺點,這是一個對象方法,當又建立了一個新的控制器,打印代碼仍是會執行(或者把狀態開關做爲本地存儲,略繁瑣,不推薦)

    2.使用dispatch_once 一次性代碼

3、隊列組

需求:從網絡上下載2個圖片,把2張圖片合併成1張最終顯示再view上 

第一種方法

 

#import "ViewController.h"
//宏定義全局併發隊列
 #define global_quque    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
//宏定義主隊列
#define main_queue       dispatch_get_main_queue()
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    _imageView1 = [[UIImageView alloc]initWithFrame:CGRectMake(0, 100, 100, 100)];
    [self.view addSubview:_imageView1];
    _imageView2 = [[UIImageView alloc]initWithFrame:CGRectMake(105, 100, 100, 100)];
    [self.view addSubview:_imageView2];
        _imageView3 = [[UIImageView alloc]initWithFrame:CGRectMake(0, 200, 200, 100)];
    [self.view addSubview:_imageView3];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    dispatch_async(global_quque, ^{
        UIImage *image1 = [self imageWithUrl:@"http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg"];
        NSLog(@"圖片1下載完成---%@",[NSThread currentThread]);
        NSLog(@"_imageView1下載完成---%@",[NSThread currentThread]);   
        UIImage *image2 = [self imageWithUrl:@"http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg"];
        NSLog(@"圖片2下載完成---%@",[NSThread currentThread]);
        NSLog(@"_imageView2下載完成---%@",[NSThread currentThread]);
        dispatch_async(main_queue, ^{     
            _imageView1.image =image1;
            _imageView2.image =image2;
            NSLog(@"顯示圖片---%@",[NSThread currentThread]);
            
            UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 1);
            [image1 drawInRect:CGRectMake(0, 0, 100, 100)];
            [image2 drawInRect:CGRectMake(100, 0, 100, 100)];
            _imageView3.image = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            NSLog(@"圖片合併完成---%@",[NSThread currentThread]);
        });
    });
}
//封裝一個方法,傳入一個url參數,返回一張網絡上下載的圖片
-(UIImage *)imageWithUrl:(NSString *)urlStr
{
    NSURL *url=[NSURL URLWithString:urlStr];
    NSData *urlData = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:urlData];
    return image;
}


    

    

    問題:這種方式效率不高,要等到圖片下載完成以後 才行.

    提示:使用隊列組能夠讓圖片1和圖片2的下載任務同時進行,且當兩個下載任務都完成的時候回到主線程進行顯示。

    2.第二種方法

    使用組隊來解決 (適用於2個耗時的操做,最後再回到主線程去執行)  

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //1.建立一個隊列組
    dispatch_group_t group =dispatch_group_create();
    //2.開啓一個任務下載圖片1
    __block UIImage *image1=nil;
    dispatch_group_async(group, global_quque, ^{
        image1 = [self imageWithUrl:@"http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg"];
        NSLog(@"圖片1下載完成---%@",[NSThread currentThread]);
    });
    
        //3.開啓一個任務下載圖片2
        __block UIImage *image2=nil;
        dispatch_group_async(group, global_quque, ^{
        image2 = [self imageWithUrl:@"http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg"];
        NSLog(@"圖片2下載完成---%@",[NSThread currentThread]);
    });
        //同時執行下載圖片1\下載圖片2操做
        //4.等group中的全部任務都執行完畢, 再回到主線程執行其餘操做
        dispatch_group_notify(group, main_queue, ^{
        NSLog(@"顯示圖片---%@",[NSThread currentThread]);
        _imageView1.image = image1;
        _imageView2.image=image2;
        
        //合併兩張圖片,最後一個參數是浮點數(0.0),不要寫成0。
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 1);
        [image1 drawInRect:CGRectMake(0, 0, 100, 100)];
        [image2 drawInRect:CGRectMake(100, 0, 100, 100)];
        _imageView3.image = UIGraphicsGetImageFromCurrentImageContext();
        
        UIGraphicsEndImageContext();
        NSLog(@"圖片合併完成---%@",[NSThread currentThread]);
        });
}

相關文章
相關標籤/搜索