GCD第二节 GCD的一个方法的使用
1.线程的通信
(1)dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{// 执⾏耗时的异步操作...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主线程,执⾏UI刷新操作
});
});
(2)[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
2.延迟提交 dispatch_after
(1)调用NSObject的方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
- (void)test1{
NSLog(@"当前线程---%@",[NSThreadcurrentThread]);
[selfperformSelector:@selector(runDtate)withObject:nilafterDelay:3.0];
/*
2016-03-08 14:00:36.071 GCDDemo3[7710:132766] 当前线程---<NSThread: 0x7fc700702160>{number = 1, name = main}
2016-03-08 14:00:39.073 GCDDemo3[7710:132766] 延迟3秒---<NSThread: 0x7fc700702160>{number = 1, name = main}
*/
}
- (void)runDtate{
NSLog(@"延迟3秒---%@",[NSThreadcurrentThread]);
}
- (void)test2{
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, ^{
NSLog(@"全局线程-%@",[NSThreadcurrentThread]);
//方法放到异步函数中不会执行,放到同步函数可以执行
[selfperformSelector:@selector(runDtate)withObject:nilafterDelay:3.0];
});
}
- (void)test3{
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_sync(queue, ^{
NSLog(@"全局线程-%@",[NSThreadcurrentThread]);
//方法放到异步函数中不会执行,放到同步函数可以执行
[selfperformSelector:@selector(runDtate)withObject:nilafterDelay:3.0];
});
/*
2016-03-08 14:07:46.468 GCDDemo3[7906:137075] 全局线程-<NSThread: 0x7f912b406510>{number = 1, name = main}
2016-03-08 14:07:49.470 GCDDemo3[7906:137075] 延迟3秒---<NSThread: 0x7f912b406510>{number = 1, name = main}
*/
}
(2)使用GCD函数
dispatch_after函数并不是在指定时间后执行处理,而只是在指定时间追加处理到Dispatch Queue 此源代码与在固定描述后,用dispatch_async函数追加到 Block到Main的相同。 这个方法不能准确的计算延迟时间,但是可以用于大概的时间延迟处理。
dispatch_after(<#dispatch_time_t when#>, <#dispatch_queue_t queue#>, <#^(void)block#>)
第一个time的解析:
dispatch_time_t time = dispatch_time(<#dispatch_time_t when#>, <#int64_t delta#>)
<#dispatch_time_t when#> 第一个参数一般是DISPATCH_TIME_NOW 表示从现在开始。
第二个参数是真正的延时的具体时间。
这里的delta是表示纳秒。
#define NSEC_PER_SEC 1000000000ull 每秒有多少纳秒
#define NSEC_PER_MSEC 1000000ull 每秒有多少毫秒
#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull 每毫秒有多少纳秒。
NSEC:纳秒。USEC:微妙 SEC:秒 PER:每
DISPATCH_TIME_NOW (int64_t)(5*NSEC_PER_SEC))从现在开始,往后数5秒。
第二个参数:
是指定要追加处理的Dispatch Queue
第三个参数:
指定要执行处理的block
- (void)test4{
dispatch_queue_t queue = queue =dispatch_queue_create("myTest1",DISPATCH_QUEUE_CONCURRENT);
NSLog(@"创建完队列");
dispatch_async(queue, ^{
[NSThreadsleepForTimeInterval:10];
NSLog(@"睡眠10s后执行");
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)), queue, ^{
NSLog(@"After 5");
});
}
/*
2016-03-14 14:37:00.076 GCDDemo3[10866:174778] 创建完队列
2016-03-14 14:37:10.082 GCDDemo3[10866:174956] 睡眠10s后执行
2016-03-14 14:37:10.083 GCDDemo3[10866:174956] After 5
*/
- (void)test5{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)),dispatch_get_main_queue(), ^{
NSLog(@"After 5");
[self delay5];
});
}
- (void)delay5{
NSLog(@"放入到after中的方法");
}
/*
2016-03-14 14:34:30.225 GCDDemo3[10782:173039] After 5
2016-03-14 14:34:30.226 GCDDemo3[10782:173039] 放入到after中的方法
*/
3. dispatch_once_t
dispatch_once函数是保证在应用程序执行中只执行一次指定处理的API。防止多个线程同时争夺同一个资源,使用dispatch_once能保证在多线程的环境下也能保证百分百的安全。
单利
+(Manager *)sharManagerData{
static Manager * shareManager =nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
shareManager = [[selfalloc]init];
});
return shareManager;
}
这里想延伸处理个问题
(1)单利
全局变量 单利可以被用作全局变量来使用
(1.1)我们可以使用AppDelegate来创建一个全局的变量(AppDelegate本身就是一个单利类)。
也可以自己单独创建一个单列类用来创建全局的变量
(1.2)使用exten创建一个全局的变量
iOS中好几个类都是采用了单例模式,比如NSApplication, NSFontManager, NSDocumentController,NSHelpManager, NSNull,NSProcessInfo, NSScriptExecutionContext, NSUserDefaults.
(2)静态变量
iOS使用静态变量
static写在interface外面编译是没有错误的,但是编译器会报警告,这么说这样的写法是不被编辑器认可的。
(2.1)两个类之间传递值
[NextViewControllerinstanceProjectid] 可以使用这种方式对静态变量projectID进行访问,无须创建实例。
这样做可以防止,多个类进入到同一个类时,会导致某个变量被其他的类修改。
//第二个类
#import <UIKit/UIKit.h>
@interface NextViewController :UIViewController
+(int)instanceProjectid;
@end
#import "NextViewController.h"
@interfaceNextViewController ()
@property (strong,nonatomic)IBOutletUITextField *textfield;
@end
//OC使用静态变量 1
static int projectID;
@implementation NextViewController
- (void)viewDidLoad {
[superviewDidLoad];
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//OC使用静态变量 2
projectID = [self.textfield.textintValue];
[selfdismissViewControllerAnimated:YEScompletion:nil];
}
//OC使用静态变量 3
+(int)instanceProjectid{
returnprojectID;
}
@end
//第一个类
#import "ViewController.h"
#import "NextViewController.h"
#import "Manager.h"
@interfaceViewController ()
@property (nonatomic,strong)UIButton * button;
@property (nonatomic,strong)UILabel * label;
@end
@implementation ViewController
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//OC使用静态变量 4
self.label.text =[NSStringstringWithFormat:@"%d",[NextViewControllerinstanceProjectid]];
}
- (void)viewDidLoad {
[superviewDidLoad];
self.label = [[UILabelalloc]initWithFrame:CGRectMake(100,200,200, 30)];
self.label.backgroundColor = [UIColorgreenColor];
[self.viewaddSubview:self.label];
self.button = [[UIButtonalloc]initWithFrame:CGRectMake(100,100,100, 40)];
self.button.backgroundColor = [UIColoryellowColor];
[self.buttonsetTitle:@"next"forState:UIControlStateNormal];
[self.buttonaddTarget:selfaction:@selector(buttonclick:)forControlEvents:UIControlEventTouchUpInside];
[self.viewaddSubview:self.button];
}
- (void)buttonclick:(UIButton *)btn{
NextViewController * next = [[NextViewControlleralloc]initWithNibName:@"NextViewController"bundle:nil];
[selfpresentViewController:nextanimated:YEScompletion:nil];
}
@end
3.dispatch_suspend/dispatch_resume挂起和恢复队列
当在queue中追加了一些处理过程,有时希望不执行已经追加的处理,可以使用这两个方法
但是dispatch_suspend函数对于已经执行的处理没有影响。挂起后,追加到queue中的处理,但是尚未执行的处理,在此之后停止执行,而恢复后则继续执行
- (void)test6{
//DISPATCH_QUEUE_SERIAL创建一个串行队列
dispatch_queue_t queue =dispatch_queue_create("myQueue",DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
[NSThreadsleepForTimeInterval:5];
NSLog(@"sleep5秒");
});
NSLog(@"挂起队列");
dispatch_suspend(queue);
NSLog(@"睡眠10秒");
[NSThreadsleepForTimeInterval:10];
dispatch_async(queue, ^{
NSLog(@"第二个queue");
});
NSLog(@"恢复队列");
dispatch_resume(queue);
}
/*
队列挂起后block还在运行正常的输出,所以,dispatch_suspend并不会立即暂停正在运行的block。
注意这里如果注释了回复队列的代码,程序是会崩溃的
2016-03-16 21:32:08.860 GCDDemo3[1272:17323] 挂起队列
2016-03-16 21:32:08.861 GCDDemo3[1272:17323] 睡眠10秒
2016-03-16 21:32:13.866 GCDDemo3[1272:17371] sleep5秒
2016-03-16 21:32:18.863 GCDDemo3[1272:17323] 恢复队列
2016-03-16 21:32:18.864 GCDDemo3[1272:17371] 第二个queue
*/
4.dispatch_apply
dispatch_apply函数是dispatch_sync函数和Dispatch Queue的关联API。该函数按照指定的次数,将指定的block追加到指定的Dispatch Queue中,并等待全部处理执行结束
dispatch_apply(<#size_t iterations#>, <#dispatch_queue_t queue#>, <#^(size_t)block#>)
第一个参数:重复的次数
第二个参数:追加的对象的Dispatch Queue
第三个参数:追加的处理
-(void)test7{
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zu",index);
});
NSLog(@"done");
}
/*
因为是在Global Queue中执行的,所以各个处理的执行时间不定,但是输出结果中最后出现的“done”必定在最后位置上。因为dispatch_apply函数会等待全部的处理执行结束。
2016-03-16 22:36:18.013 GCDDemo3[1388:15648] 1
2016-03-16 22:36:18.013 GCDDemo3[1388:15647] 2
2016-03-16 22:36:18.013 GCDDemo3[1388:15588] 0
2016-03-16 22:36:18.013 GCDDemo3[1388:15651] 3
2016-03-16 22:36:18.014 GCDDemo3[1388:15588] 6
2016-03-16 22:36:18.014 GCDDemo3[1388:15648] 4
2016-03-16 22:36:18.014 GCDDemo3[1388:15647] 5
2016-03-16 22:36:18.014 GCDDemo3[1388:15651] 7
2016-03-16 22:36:18.014 GCDDemo3[1388:15588] 8
2016-03-16 22:36:18.014 GCDDemo3[1388:15648] 9
2016-03-16 22:36:18.015 GCDDemo3[1388:15588] done
*/
因为dispatch_apply函数和dispatch_sync一样,会等待处理执行的结果。所以建议把在dispatch_async中执行非同步的dispatch_apply函数。
- (void)test8{
NSArray * array =@[@"第一个",@"第二个",@"第三个",@"第四个",@"第五个"];
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, ^{
dispatch_apply([arraycount], queue, ^(size_t index) {
NSLog(@"%zu,%@",index,[arrayobjectAtIndex:index]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主线程");
});
});
/*
这个可以把前面写的下载多张图片的代码整合一下了。执行的顺序是不确定的
2016-03-16 22:54:17.405 GCDDemo3[1868:23796] 2,第三个
2016-03-16 22:54:17.405 GCDDemo3[1868:23797] 0,第一个
2016-03-16 22:54:17.405 GCDDemo3[1868:23803] 3,第四个
2016-03-16 22:54:17.405 GCDDemo3[1868:23795] 1,第二个
2016-03-16 22:54:17.406 GCDDemo3[1868:23796] 4,第五个
2016-03-16 22:54:17.413 GCDDemo3[1868:23776] 回到主线程
*/
}
5.dispatch_group
使用的例子:
例如,我们遇到一个界面多个请求,但是必须等到这多个请求全部结束的时候,才能展示界面的数据,这是我们就可以使用dispatch_group来帮住我们解决这个问题;
在追加到dispatch_queue中的多个处理全部结束后想执行结束处理,这个时候我们就可以有两种处理方法
(1)串行队列,把向执行都放到串行队列中,在最后追加结束后处理即可
(2)使用并发队列,并且同时多个,这种情况下就可以使用Dispatch Group
dispatch_group的使用步骤
(1)创建dispatch_group_t 创建一个group 使用的是dispatch_group_create()
(2)添加任务
(3)结束任务
添加任务:
1.自己创建队列 dispatch_group_async
无法直接使用队列变量
2.参考文章 http://blog.csdn.net/majiakun1/article/details/38642833
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//Enter group
dispatch_group_enter(group);
[manager GET:@"http://www.baidu.com" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//Deal with result...
//Leave group
dispatch_group_leave(group);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//Deal with error...
//Leave group
dispatch_group_leave(group);
}];
//More request...
使用dispatch_group_enter,dispatch_group_leave就可以方便的将一系列网络请求“打包”起来~
3,添加结束任务
(1)在当前线程阻塞的同步等待:dispatch_group_wait;等待全部执行结束
第二个参数:超时时间
#define DISPATCH_TIME_NOW (0ull)
#define DISPATCH_TIME_FOREVER (~0ull)永久等待
dispatch_group_wait(<#dispatch_group_t group#>, <#dispatch_time_t timeout#>)
- (void)test10{
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_t group =dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"test10----1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"test10----2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"test10----3");
});
dispatch_group_async(group, queue, ^{
NSLog(@"test10----4");
});
dispatch_group_async(group, queue, ^{
NSLog(@"test10----5");
});
dispatch_group_async(group, queue, ^{
NSLog(@"test10----6");
});
dispatch_group_async(group, queue, ^{
NSLog(@"test10----7");
});
dispatch_group_async(group, queue, ^{
NSLog(@"test10----8");
});
dispatch_group_async(group, queue, ^{
NSLog(@"test10----9");
});
dispatch_group_async(group, queue, ^{
NSLog(@"test10----10");
});
dispatch_time_t time =dispatch_time(DISPATCH_TIME_NOW,1 * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
if(result == 0){
NSLog(@"done");
}else{
NSLog(@"还没有执行完成");
}
}
/*
2016-03-18 13:32:41.880 GCDDemo3[16405:121386] test10----1
2016-03-18 13:32:41.881 GCDDemo3[16405:121391] test10----5
2016-03-18 13:32:41.880 GCDDemo3[16405:121385] test10----2
2016-03-18 13:32:41.880 GCDDemo3[16405:121387] test10----3
2016-03-18 13:32:41.880 GCDDemo3[16405:121390] test10----4
2016-03-18 13:32:41.881 GCDDemo3[16405:121392] test10----6
2016-03-18 13:32:41.881 GCDDemo3[16405:121386] test10----7
2016-03-18 13:32:41.881 GCDDemo3[16405:121391] test10----8
2016-03-18 13:32:41.881 GCDDemo3[16405:121387] test10----9
2016-03-18 13:32:41.881 GCDDemo3[16405:121385] test10----10
2016-03-18 13:32:41.882 GCDDemo3[16405:121206] done
*/
dispatch_group_wait函数的返回值不是0,就意味这虽然经历了指定的时间,但Dispatch Group的某个处理还在执行中。如果返回值是0,那么全部执行完成了。当等待时间为DISPATCH_TIME_FOREVER 由于属于Dispatch Group的处理必定全部执行完成了。因此返回值会恒为0;
(2)添加一个异步执行的任务作为结束任务后:dispatch_group_notify
- (void)test9{
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
创建一个group
dispatch_group_t group =dispatch_group_create();
添加任务
dispatch_group_async(group, queue, ^{
NSLog(@"group1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"group2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"group3");
});
dispatch_group_async(group, queue, ^{
NSLog(@"group4");
});
dispatch_group_async(group, queue, ^{
NSLog(@"group5");
});
dispatch_group_notify(group,dispatch_get_main_queue(), ^{
NSLog(@"done");
});
/*
创建一个全局的队列,创建一个group, 把队列加入到group中,在group的异步函数中执行;
016-03-17 22:39:59.354 GCDDemo3[2271:23666] group2
2016-03-17 22:39:59.354 GCDDemo3[2271:23667] group1
2016-03-17 22:39:59.354 GCDDemo3[2271:23669] group4
2016-03-17 22:39:59.354 GCDDemo3[2271:23668] group3
2016-03-17 22:39:59.354 GCDDemo3[2271:23670] group5
2016-03-17 22:39:59.359 GCDDemo3[2271:23626] done
*/
}
6. dispatch_sync 同步
同步很容易就引起死锁,这是不可避免的。在主线程中使用同步方法,必然会引起死锁
dispatch_apply和dispatch_sync同理,也很容易引起死锁;
7.dispatch_barrier_async
dispatch_barrier_async的作用就是在某个队列中插入一个block,当目前正在执行的block运行完毕后,阻塞这个block后面添加的block,只运行这个block直到完成,然后再继续后续的任务。
使用concurrent dispatch queue 和 dispatch barrier async 可以实现高效的数据库访问和文件访问。
这个我还没有遇到真是的案例。有待后续学习。
-(void)test11{
dispatch_queue_t queue =dispatch_queue_create("myTest11",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"queue1");
});
dispatch_async(queue, ^{
NSLog(@"queue2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"插入的操作");
});
dispatch_barrier_async(queue, ^{
NSLog(@"queue3");
});
}
/*
dispatch_barrier_async这个方法的提出是为了解决一个问题,那就是在某些任务完成后就执行指定的任务,该方法会阻塞队列中未执行的任务。
2016-03-21 22:28:45.342 GCDDemo3[894:11775] queue2
2016-03-21 22:28:45.342 GCDDemo3[894:11774] queue1
2016-03-21 22:28:45.342 GCDDemo3[894:11774] 插入的操作
2016-03-21 22:28:45.342 GCDDemo3[894:11774] queue3
*/
dispatch_barrier_async/dispatch_barrier_sync只在自己创建的并发队列中有效,在全局并发队列、串行队列中效果和dispatch_async/dispatch_sync相同
8.dispatch_set_context
这部分内没有实战的操作,在这查找了网上的一些资料
dispatch_set_context可以为队列添加上下文数据,但是因为GCD是C语言接口形式的,所以其context参数类型是“void *”。也就是说,我们创建context时有如下几种选择:
(1)用C语言的malloc创建context数据。
(2)用C++的new创建类对象。
(3)用Objective-C的对象,但是要用__bridge等关键字转为Core Foundation对象。
以上所有创建context的方法都有一个必须的要求,就是都要释放内存!,无论是用free、delete还是CF的CFRelease,我们都要确保在队列不用的时候,释放context的内存,否则就会造成内存泄露。
所以,使用dispatch_set_context的时候,最好结合dispatch_set_finalizer_f使用,为队列设置“析构函数”,在这个函数里面释放内存,大致如下:
void cleanStaff(void *context) {
//释放context的内存!
//CFRelease(context);
//free(context);
//delete context;
}
...
//在队列创建后,设置其“析构函数”
dispatch_set_finalizer_f(queue, cleanStaff);
9.Dispatch Semaphore
当并行执行的处理 更新数据时,会产生数据不一致的情况,这个问题的解决可以使用Serial Dispatch Queue和Dispatch_barrier_async 这个函数。
-(void)test12{
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
NSMutableArray * array = [[NSMutableArrayalloc]init];
for(int i =0; i<10000;i++){
dispatch_async(queue, ^{
[array addObject:[NSNumbernumberWithInt:i]];
});
}
}
/*
malloc: *** error for object 0x7ffe60506550: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
由于内存导致的应用程序结束。我们可以修改为test13的版本
*/
-(void)test13{
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_semaphore_t semaphore =dispatch_semaphore_create(1);
NSMutableArray * array = [[NSMutableArrayalloc]init];
for(int i =0;i<10000;i++){
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
[array addObject:[NSNumbernumberWithInt:i]];
dispatch_semaphore_signal(semaphore);
});
}
NSLog(@"array--%@",array);
NSLog(@"donw");
}
/*
2016-03-23 13:59:49.744 GCDDemo3[8278:164595] array--(
0,
到
9999
)
2016-03-23 13:59:49.877 GCDDemo3[8278:164595] donw
*/
dispatch_semaphore是持有计数信号,该计数是多线程编程中的计数类型信号。当计数为0时等待,计数为1或大于1时,减去1而不等待。
(1)创建
dispatch_semaphore_t semaphore =dispatch_semaphore_create(1);
(2)信号的返回
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
等待计数值达到大于后等于1,当计数值大于等于1,或者在待机中计数值等于1时对该计数值进行减法并从该函数中返回。返回值为0,可以安全的执行需要进行排他控制的处理,该处理结束的时候通过dispatch_semaphore_signal(semaphore);将dispatch_semaphore的计数加1;
10.还有一个比较次要点的Dispatch Sources
http://blog.csdn.net/nogodoss/article/details/31346207
可以看看