例如下载网页功能。
如果不用GCD,多线程下载,我们要把下载过程放到后台,以免阻塞UI。需要以下三部:
1.someClick:单击按钮后,我们用NSInvocationOperarion建立一个后台线程,并且把这个线程放到NSOperartionQueue中。后台执行download方法。
2 download方法处理下载网页的逻辑。下载完了之后用performSelectorOnMainThread(回到主线程)执行download_completed方法.
3.download_completed 进行clear up工作,把下载的内容显示到文本控件中.(这里我理解的是刷新UI的操作).
static NSOperationQueue *queue;
- (IBAction)someClick:(id)sender {
// self.indicator.hidden = NO; //关于下载过程中圈圈的处理
// [self.indicator startAnimating];
queue = [[NSOperationQueue alloc]init]; //创建操作队列
NSInvocationOperation * op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil]; //创建调用操作
[queue addOperation:op]; //加入队列
}
-(void)download {
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
NSError *error;
NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (data != nil) {
[self performSelectorOnMainThread:@selector(download_completed:) withObject:data waitUntilDone:NO]; //回到主线程 执行下载完成操作
}else {
NSLog(@"error when download:%@",error);
}
}
- (void)download_completed:(NSString *)data { //根据下载完传回的data 做UI刷新显示.
NSLog(@"call back");
[self.indicator stopAnimating];
self.indicator.hidden = YES;
self.content.text = data;
}
当不用GCD的时候,下载被分成了三个步骤: 开始下载->下载中->下载完成.这三个方法还要互相传递参数,而当传递的参数过于复杂的时候,
就要用到model类,把下载放到model类中做,而界面控制放到viewcontroller层来做,这使得代码变得更加零散.
使用GCD
//代码块一
self.indicator.hidden = NO; //关于下载过程中圈圈的处理
[self.indicator startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//代码块二
NSURL *url = [NSURL URLWithString:@"http://baidu.com"];
NSError *error;
NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (data != nil) {
dispatch_async(dispatch_get_main_queue(), ^{
//代码块三
[self.indicator stopAnimating];
self.indicator.hidden = YES;
self.content.text = data;
});
}else {
NSLog(@"error when download:%@",error);
}
});
block定义
//声明变量
-(void)(^loggerBlock)(void);
//定义
loggerBlock = ^{
NSLog(@"hello");
};
//调用
loggerBlock();
内联定义block
即将他的程序块写在调用的函数里面
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//something
});
block特点
1.程序块可以在代码中医内联的方式定义
2.程序块可以访问在创建它的范围内的可用变量.
GCD中用了dispatch方法
这事为了方便我们使用GCD,苹果提供了一些方法方便我们将block放在主线程或后台线程执行,或者延后执行.
//后台执行
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//something
});
//主线程执行
dispatch_async(dispatch_get_main_queue(), ^{
//something
})
//一次性执行
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//code to be executed once
});
//延迟两秒执行
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds*NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^{
//code to be executed on the main queue after delay
});
GCD高级用法
让后台两个线程并行执行,然后等两个线程都结束后,在汇总执行结果.
这个可以用 dispatch_group, dispatch_group_async,dispatch_group_notify来实现
修改block之外的变量
默认情况下,block程序块中的外部变量是复制过去的,即 写操作不对原变量生效,但是外部变量用__block修饰可以让写操作生效.
__block int a = 0;
void (^foo)(void) = ^ {
a = 1;
}
foo(); //此时a的值被修改为1;
后台运行
使用block的另一个用处就是可以让程序在后台叫长久的运行,
在应用按Home键之后 应用可以调用UIApplication的beginBackgroundTaskWithExpirationHandler方法,让应用有最多十分钟的时间在后台长久的运行.这个时间可以用来做清理本地缓存.发送统计数据等工作.
// AppDelegate.h
@property(assign,nonatomic)UIBackgroundTaskIdentifier backgroundUpdateTask;
// AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application {
[self beingBackgroundUpdateTask];
//这里填写后台需要运行的代码
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
[self endBackgroundUpdateTask];
}
-(void)beingBackgroundUpdateTask {
self.backgroundUpdateTask = [[UIApplication sharedApplication]beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}
-(void)endBackgroundUpdateTask {
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundUpdateTask];
self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.