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

一、延遲執行
1.介紹
iOS常見的延時執行有2種方式
(1)調用NSObject的方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 2秒後再調用self的run方法
 
(2)使用GCD函數
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 2秒後異步執行這裏的代碼...
});
2.說明
第一種方法,該方法在那個線程調用,那麼run就在哪個線程執行(當前線程),通常是主線程。
[self performSelector:@selector(run) withObject:nil afterDelay:3.0];
說明:在3秒鐘之後,執行run函數
代碼示例:
 1 //
 2 //  YYViewController.m
 3 //  01-GCD的常見使用(延遲執行)
 4 //
 5 //  Created by apple on 14-6-25.
 6 //  Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8 
 9 #import "YYViewController.h"
10 
11 @interface YYViewController ()
12 
13 @end
14 
15 @implementation YYViewController
16 
17 - (void)viewDidLoad
18 {
19     [super viewDidLoad];
20     NSLog(@"打印線程----%@",[NSThread currentThread]);
21     //延遲執行
22     //第一種方法:延遲3秒鐘調用run函數
23     [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
24     
25 }
26 -(void)run
27 {
28     NSLog(@"延遲執行----%@",[NSThread currentThread]);
29 }
30 
31 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
32 {
33     //在異步函數中執行
34     dispatch_queue_t queue = dispatch_queue_create("wendingding"0);
35     
36     dispatch_sync(queue, ^{
37         [self performSelector:@selector(test) withObject:nil afterDelay:1.0];
38     });
39     NSLog(@"異步函數");
40 }
41 -(void)test
42 {
43     NSLog(@"異步函數中延遲執行----%@",[NSThread currentThread]);
44 }
45 @end
說明:如果把該方法放在異步函數中執行,則方法不會被調用(BUG?)
第二種方法,
 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       //延遲執行的方法
    });
說明:在5秒鐘之後,執行block中的代碼段。
參數說明:
什麼時間,執行這個隊列中的這個任務。
代碼示例:
 1 //
 2 //  YYViewController.m
 3 //  02-GCD常見使用(延遲執行2)
 4 //
 5 //  Created by apple on 14-6-25.
 6 //  Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8 
 9 #import "YYViewController.h"
10 
11 @interface YYViewController ()
12 
13 @end
14 
15 @implementation YYViewController
16 
17 - (void)viewDidLoad
18 {
19     [super viewDidLoad];
20 
21     NSLog(@"打印當前線程---%@",  [NSThread currentThread]);
22     
23     //延遲執行,第二種方式
24      //可以安排其線程(1),主隊列
25      dispatch_queue_t queue= dispatch_get_main_queue();
26     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
27         NSLog(@"主隊列--延遲執行------%@",[NSThread currentThread]);
28     });
29     
30     //可以安排其線程(2),併發隊列
31     //1.獲取全局併發隊列
32     dispatch_queue_t queue1= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
33     //2.計算任務執行的時間
34     dispatch_time_t when=dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
35     //3.會在when這個時間點,執行queue中的這個任務
36     dispatch_after(when, queue1, ^{
37         NSLog(@"併發隊列-延遲執行------%@",[NSThread currentThread]);
38     });
39 }
40 
41 @end
延遲執行:不需要再寫方法,且它還傳遞了一個隊列,我們可以指定並安排其線程。
如果隊列是主隊列,那麼就在主線程執行,如果隊列是併發隊列,那麼會新開啓一個線程,在子線程中執行。
 
二、一次性代碼
1.實現一次性代碼
需求:點擊控制器只有第一次點擊的時候纔打印。
實現代碼:
 1 //
 2 //  YYViewController.m
 3 //  03-GCD常見使用(一次性代碼)
 4 //
 5 //  Created by apple on 14-6-25.
 6 //  Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8 
 9 #import "YYViewController.h"
10 
11 @interface YYViewController ()
12 @property(nonatomic,assign) BOOL log;
13 @end
14 
15 @implementation YYViewController
16 
17 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
18 {
19     if (_log==NO) {
20         NSLog(@"該行代碼只執行一次");
21         _log=YES;
22     }
23 }
24 @end
缺點:這是一個對象方法,如果又創建一個新的控制器,那麼打印代碼又會執行,因爲每個新創建的控制器都有自己的布爾類型,且新創建的默認爲NO,因此不能保證改行代碼在整個程序中只打印一次。
2.使用dispatch_once一次性代碼
使用dispatch_once函數能保證某段代碼在程序運行過程中只被執行1次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只執行1次的代碼(這裏面默認是線程安全的)
});
整個程序運行過程中,只會執行一次。
代碼示例:
 1 //
 2 //  YYViewController.m
 3 //  03-GCD常見使用(一次性代碼)
 4 //
 5 //  Created by apple on 14-6-25.
 6 //  Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8 
 9 #import "YYViewController.h"
10 
11 @interface YYViewController ()
12 @property(nonatomic,assign) BOOL log;
13 @end
14 
15 @implementation YYViewController
16 
17 //-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
18 //{
19 //    if (_log==NO) {
20 //        NSLog(@"該行代碼只執行一次");
21 //        _log=YES;
22 //    }
23 //}
24 
25 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
26 {
27     static dispatch_once_t onceToken;
28     dispatch_once(&onceToken, ^{
29         NSLog(@"該行代碼只執行一次");
30     });
31 }
32 @end
效果(程序運行過程中,打印代碼只會執行一次):
三、隊列組
需求:從網絡上下載兩張圖片,把兩張圖片合併成一張最終顯示在view上。
1.第一種方法
代碼示例:
 1 //
 2 //  YYViewController.m
 3 //  04-GCD基本使用(隊列組下載圖片)
 4 //
 5 //  Created by apple on 14-6-25.
 6 //  Copyright (c) 2014年 itcase. All rights reserved.
 7 //
 8 
 9 #import "YYViewController.h"
10 //宏定義全局併發隊列
11 #define global_quque    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
12 //宏定義主隊列
13 #define main_queue       dispatch_get_main_queue()
14 
15 @interface YYViewController ()
16 @property (weak, nonatomic) IBOutlet UIImageView *imageView1;
17 @property (weak, nonatomic) IBOutlet UIImageView *imageView2;
18 @property (weak, nonatomic) IBOutlet UIImageView *imageView3;
19 
20 @end
21 
22 @implementation YYViewController
23 
24 - (void)viewDidLoad
25 {
26     [super viewDidLoad];
27 }
28 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
29 {
30     //獲取全局併發隊列
31 //    dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
32     //獲取主隊列
33 //    dispatch_queue_t queue= dispatch_get_main_queue();
34     
35 //    圖片1:http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg
36 //    圖片2:http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg
37     dispatch_async(global_quque, ^{
38         //下載圖片1
39        UIImage *image1= [self imageWithUrl:@"http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg"];
40         NSLog(@"圖片1下載完成---%@",[NSThread currentThread]);
41     
42         //下載圖片2
43        UIImage *image2= [self imageWithUrl:@"http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg"];
44         NSLog(@"圖片2下載完成---%@",[NSThread currentThread]);
45         
46         //回到主線程顯示圖片
47         dispatch_async(main_queue, ^{
48              NSLog(@"顯示圖片---%@",[NSThread currentThread]);
49             self.imageView1.image=image1;
50             self.imageView2.image=image2;
51             //合併兩張圖片
52             UIGraphicsBeginImageContextWithOptions(CGSizeMake(200100), NO, 0.0);
53             [image1 drawInRect:CGRectMake(00100100)];
54             [image2 drawInRect:CGRectMake(1000100100)];
55             self.imageView3.image=UIGraphicsGetImageFromCurrentImageContext();
56             //關閉上下文
57             UIGraphicsEndImageContext();
58                NSLog(@"圖片合併完成---%@",[NSThread currentThread]);
59         });
60         //
61     });
62 }
63 
64 //封裝一個方法,傳入一個url參數,返回一張網絡上下載的圖片
65 -(UIImage *)imageWithUrl:(NSString *)urlStr
66 {
67     NSURL *url=[NSURL URLWithString:urlStr];
68     NSData *data=[NSData dataWithContentsOfURL:url];
69     UIImage *image=[UIImage imageWithData:data];
70     return image;
71 }
72 @end
顯示效果:
打印查看:
問題:這種方式的效率不高,需要等到圖片1.圖片2都下載完成後才行。
提示:使用隊列組可以讓圖片1和圖片2的下載任務同時進行,且當兩個下載任務都完成的時候回到主線程進行顯示。
2.使用隊列組解決
步驟:
創建一個組
開啓一個任務下載圖片1
 開啓一個任務下載圖片2
同時執行下載圖片1\下載圖片2操作
等group中的所有任務都執行完畢, 再回到主線程執行其他操作
代碼示例
  1 //
  2 //  YYViewController.m
  3 //  04-GCD基本使用(隊列組下載圖片)
  4 //
  5 //  Created by apple on 14-6-25.
  6 //  Copyright (c) 2014年 itcase. All rights reserved.
  7 //
  8 
  9 #import "YYViewController.h"
 10 //宏定義全局併發隊列
 11 #define global_quque    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
 12 //宏定義主隊列
 13 #define main_queue       dispatch_get_main_queue()
 14 
 15 @interface YYViewController ()
 16 @property (weak, nonatomic) IBOutlet UIImageView *imageView1;
 17 @property (weak, nonatomic) IBOutlet UIImageView *imageView2;
 18 @property (weak, nonatomic) IBOutlet UIImageView *imageView3;
 19 
 20 @end
 21 
 22 @implementation YYViewController
 23 
 24 - (void)viewDidLoad
 25 {
 26     [super viewDidLoad];
 27 }
 28 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 29 {
 30     //    圖片1:http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg
 31     //    圖片2:http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg
 32     
 33     
 34     //1.創建一個隊列組
 35         dispatch_group_t group = dispatch_group_create();
 36     
 37     //2.開啓一個任務下載圖片1
 38     __block UIImage *image1=nil;
 39     dispatch_group_async(group, global_quque, ^{
 40         image1= [self imageWithUrl:@"http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg"];
 41         NSLog(@"圖片1下載完成---%@",[NSThread currentThread]);
 42     });
 43     
 44     //3.開啓一個任務下載圖片2
 45     __block UIImage *image2=nil;
 46     dispatch_group_async(group, global_quque, ^{
 47         image2= [self imageWithUrl:@"http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg"];
 48         NSLog(@"圖片2下載完成---%@",[NSThread currentThread]);
 49     });
 50     
 51     //同時執行下載圖片1\下載圖片2操作
 52     
 53    //4.等group中的所有任務都執行完畢, 再回到主線程執行其他操作
 54     dispatch_group_notify(group,main_queue, ^{
 55         NSLog(@"顯示圖片---%@",[NSThread currentThread]);
 56         self.imageView1.image=image1;
 57         self.imageView2.image=image2;
 58         
 59         //合併兩張圖片
 60         //注意最後一個參數是浮點數(0.0),不要寫成0。
 61         UIGraphicsBeginImageContextWithOptions(CGSizeMake(200100), NO, 0.0);
 62         [image1 drawInRect:CGRectMake(00100100)];
 63         [image2 drawInRect:CGRectMake(1000100100)];
 64         self.imageView3.image=UIGraphicsGetImageFromCurrentImageContext();
 65         //關閉上下文
 66         UIGraphicsEndImageContext();
 67         
 68         NSLog(@"圖片合併完成---%@",[NSThread currentThread]);
 69     });
 70     
 71 }
 72 -(void)download2image
 73 {
 74     //獲取全局併發隊列
 75 //    dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 76     //獲取主隊列
 77 //    dispatch_queue_t queue= dispatch_get_main_queue();
 78     
 79     dispatch_async(global_quque, ^{
 80         //下載圖片1
 81        UIImage *image1= [self imageWithUrl:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
 82         NSLog(@"圖片1下載完成---%@",[NSThread currentThread]);
 83     
 84         //下載圖片2
 85        UIImage *image2= [self imageWithUrl:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
 86         NSLog(@"圖片2下載完成---%@",[NSThread currentThread]);
 87         
 88         //回到主線程顯示圖片
 89         dispatch_async(main_queue, ^{
 90              NSLog(@"顯示圖片---%@",[NSThread currentThread]);
 91             self.imageView1.image=image1;
 92             self.imageView2.image=image2;
 93             //合併兩張圖片
 94             UIGraphicsBeginImageContextWithOptions(CGSizeMake(200100), NO, 0.0);
 95             [image1 drawInRect:CGRectMake(00100100)];
 96             [image2 drawInRect:CGRectMake(00100100)];
 97             self.imageView3.image=UIGraphicsGetImageFromCurrentImageContext();
 98             //關閉上下文
 99             UIGraphicsEndImageContext();
100                NSLog(@"圖片合併完成---%@",[NSThread currentThread]);
101         });
102         //
103     });
104 }
105 
106 //封裝一個方法,傳入一個url參數,返回一張網絡上下載的圖片
107 -(UIImage *)imageWithUrl:(NSString *)urlStr
108 {
109     NSURL *url=[NSURL URLWithString:urlStr];
110     NSData *data=[NSData dataWithContentsOfURL:url];
111     UIImage *image=[UIImage imageWithData:data];
112     return image;
113 }
114 @end
打印查看(同時開啓了兩個子線程,分別下載圖片):
2.補充說明
有這麼1種需求:
首先:分別異步執行2個耗時的操作
其次:等2個異步操作都執行完畢後,再回到主線程執行操作
 
如果想要快速高效地實現上述需求,可以考慮用隊列組
dispatch_group_t group =  dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 執行1個耗時的異步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 執行1個耗時的異步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 等前面的異步操作都執行完畢後,回到主線程...
});

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章