GCD6: 在GCD上异步执行非UI相关任务

讨论:在主队列、串行队列和并发队列上异步执行代码块才能见识到 GCD 的真正实力。

要在分派队列上执行异步任务,你必须使用下面这些函数中的其中一个:

dispatch_async为了异步执行向分派队列提交一个 Block Object(2 项都通过参数指定)

dispatch_async_f为了异步执行向分派队列提交一个 C 函数和一个上下文引用(3 项通过参数参数指定) 

dispatch_async 和dispatch_sync的区别

dispatch_sync(),同步添加操作。他是等待添加进队列里面的操作完成之后再继续执行。

dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    dispatch_sync(concurrentQueue, ^(){
        NSLog(@"2");
        [NSThread sleepForTimeInterval:10];
        NSLog(@"3");
    });
    NSLog(@"4");
输出 : 
       
11:36:25.313 GCDSeTest[544:303] 1
11:36:25.313 GCDSeTest[544:303] 2
11:36:30.313 GCDSeTest[544:303] 3//模拟长时间操作
11:36:30.314 GCDSeTest[544:303] 4

dispatch_async ,异步添加进任务队列,它不会做任何等待

dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"2");
        [NSThread sleepForTimeInterval:5];
        NSLog(@"3");
    });
    NSLog(@"4");
输出:
 11:42:43.820 GCDSeTest[568:303] 1
 11:42:43.820 GCDSeTest[568:303] 4
 11:42:43.820 GCDSeTest[568:1003] 2
 11:42:48.821 GCDSeTest[568:1003] 3//模拟长时间操作时间

实例:

写一个 iOS APP, 它能从网络的 URL 上下载一个图片。下载完 成之后,APP 应该将图片展示给用户。 

 

1、我们在并发队列上异步的启动一个 block object。

2、在这个 block 中我们使用 dispatch_sync 函数来启动另一个 Block Object,以从 URL 上下载图片。我这样做的目的是想让并发队列中剩余的代码能够等到图片下载完成之后在继续执行。从一个异步代码块上同步下载一个 URL 可以保持这个队列运行同步函数,而不是主线程。当我们从主线程角度来看的话整个运作仍是异步的。我们关心的问题是在下载图片的过程中没有阻塞主线程。

3、图片下载完毕后,为了在 UI 上将图片展示给用户,我们会在主线程上同步执行一个 Block 

 

我们计划的框架:
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
dispatch_async(concurrentQueue, ^{ __block UIImage *image = nil; dispatch_sync(concurrentQueue, ^{ /* Download the image here */ }); dispatch_sync(dispatch_get_main_queue(), ^{ /* Show the image to the user here on the main queue*/ }); });

详细代码为:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(concurrentQueue, ^{
        __block UIImage *image = nil;
        dispatch_sync(concurrentQueue, ^{
            //开始下载图片
            NSString *urlString = @"http://a.hiphotos.baidu.com/image/pic/item/eac4b74543a9822677de5a5c8982b9014a90ebaa.jpg";
            NSURL *url = [NSURL URLWithString:urlString];
            NSURLRequest *request = [NSURLRequest requestWithURL:url];
            NSError *downloadError = nil;
            NSData *imageData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&downloadError];
            if (downloadError == nil && imageData != nil) {
                image = [UIImage imageWithData:imageData];
                //获得了图片
            }
            else if (downloadError != nil){
                NSLog(@"Error happended : %@",downloadError);
            }else{
                NSLog(@"No data could get downloaded from the URL");
            }
        });
        dispatch_sync(dispatch_get_main_queue(), ^{
            //主线程上展示图片
            if (image != nil) {
                UIImageView *imageView = [[UIImageView alloc]initWithFrame:self.view.bounds];
                [imageView setImage:image];
                [imageView setContentMode:UIViewContentModeScaleAspectFit];
                [self.view addSubview:imageView];
            }else{
                NSLog(@"Image isn't downloaded. Nothing to display.");
            }
        });
    });
}

下面我们来看另一个例子:

假设我们在磁盘的一个文件内存储了 1000 个随机数字的数组,我们想把这个数组加载到内存,让数字按照升序排列然后展示给用户。
如果我们没有数组,那为什么我们不先创建一个数组然后再进行加载,最后展示出来呢? 

 下面两个方法帮助我们在设备上找某文件

- (NSString *)fileLocation{
    //获得文件夹位置
    NSArray *folders = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);//见详解1
    if ([folders count] == 0) {
        return nil;
    }
    //找到了
    NSString *documentsFolder = [folders objectAtIndex:0];
    //返回文件目录  .../list.txt
    return [documentsFolder stringByAppendingPathComponent:@"list.txt"];
}
- (BOOL) hasFileAlreadyBeenCreated{
    BOOL result = NO;
    NSFileManager *fileManager = [[NSFileManager alloc]init];
    //判断.../list.txt文件是否已经创建
    if ([fileManager fileExistsAtPath:[self fileLocation]]) {
        return YES;
    }
    return result;
}

如果没有我们自己创建一个数组并存入磁盘,然后再从磁盘读出该数组,对该数组里的数进行排序显示:

- (void)fixNumbers{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(concurrentQueue, ^{
        NSUInteger numberOfValuesRequired = 1000;
        if ([self hasFileAlreadyBeenCreated] == NO) {
            //如果没有这1000个随机数的数组,我们创建一个
            dispatch_sync(concurrentQueue, ^{
                NSMutableArray *arrayOfRandomNumbers = [[NSMutableArray alloc]initWithCapacity:numberOfValuesRequired];
                for (NSUInteger counter = 0; counter < numberOfValuesRequired; counter++) {
                    //unsigned int randomNumber = arc4random()%((unsigned int)RAND_MAX + 1);
                    unsigned int randomNumber = arc4random()%1000;
                    [arrayOfRandomNumbers addObject:[NSNumber numberWithUnsignedInt:randomNumber]];
                }
                //然后将这个数组存入磁盘
                /*BOOL isWrite = [arrayOfRandomNumbers writeToFile:[self fileLocation] atomically:YES];
                NSLog(@"%d",isWrite);
                 这句代码打印出的是0 没有写入磁盘. 应该是因为arrayOfRandomNumbers是可变数组不能够写入. 要将它转成NSArray
                 */

                NSArray *writeArray = [NSArray arrayWithArray:arrayOfRandomNumbers];
                BOOL isWrite = [writeArray writeToFile:[self fileLocation] atomically:YES];
                NSLog(@"%d",isWrite);
            });
        }
        __block NSMutableArray *randomNumbers = nil;
        //在磁盘中读取这些数并且排序
        dispatch_sync(concurrentQueue, ^{
            if ([self hasFileAlreadyBeenCreated]) {
                
                //读取
                randomNumbers = [[NSMutableArray alloc]initWithContentsOfFile:[self fileLocation]];
                NSLog(@"%@",[self fileLocation]);
                //排序
                [randomNumbers sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
                    NSNumber *number1 = (NSNumber *)obj1;
                    NSNumber *number2 = (NSNumber *)obj2;
                    return [number1 compare:number2];
                }];
            }
        });
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([randomNumbers count] > 0) {
                for (int i = 0; i < randomNumbers.count; i++) {
                    NSLog(@"%d",[(NSNumber *)[randomNumbers objectAtIndex:i] unsignedIntValue]);
                }
            }
        });
    });
}

 

详解1: 

iOS中NSSearchPathForDirectoriesInDomains()方法的学习:

FOUNDATION_EXPORT NSArray *NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);

 第一个参数NSSearchPathDirectory directory 在iOS中常用NSDocumentDirectory , NSDocumentationDirectory  ,NSDownloadsDirectory这三个参数.下面是测试结果

NSDocumentDirectory

-(NSString *) dataFilePath { 
     NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
      NSString *documentsDirectory=[paths objectAtIndex:0];
      return [documentsDirectory stringByAppendingPathComponent:@"data.plist"];
 }

return value:
path:/Users/admin/Library/Application Support/iPhone Simulator/5.0/Applications/4BC5DA66-B3CA-4056-927B-999BC4DBF3CE/Documents/data.plist

NSDocumentationDirectory

-(NSString *) dataFilePath {
   NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory , NSUserDomainMask, YES); 
   NSString *documentsDirectory=[paths objectAtIndex:0];
   return [documentsDirectory stringByAppendingPathComponent:@"data.plist" ];
 }

return value:
path:/Users/admin/Library/Application Support/iPhone Simulator/5.0/Applications/4BC5DA66-B3CA-4056-927B-999BC4DBF3CE/Library/Documentation/data.plist

NSDownloadsDirectory

-(NSString *) dataFilePath { 
  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSUserDomainMask, YES); 
  NSString *documentsDirectory=[paths objectAtIndex:0]; 
  return [documentsDirectory stringByAppendingPathComponent:@"data.plist" ];
 }

return value:
path:/Users/admin/Library/Application Support/iPhone Simulator/5.0/Applications/4BC5DA66-B3CA-4056-927B-999BC4DBF3CE/Downloads/data.plist
 

总结:

因为应用是在沙箱(sandbox)中的,在文件读写权限上受到限制,只能在几个目录下读写文件:

  • Documents:应用中用户数据可以放在这里,iTunes备份和恢复的时候会包括此目录
  • tmp:存放临时文件,iTunes不会备份和恢复此目录,此目录下文件可能会在应用退出后删除
  • Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下文件不会在应用退出删除

 


posted @ 2014-11-05 11:54  safiri  阅读(441)  评论(0编辑  收藏  举报