zoukankan      html  css  js  c++  java
  • iOS多线程系列(3)

            在2011的WWDC上,苹果推出了GCD,从此多线程增加了一种新的方法。GCD要求运行在iOS4.0版本以上或者OS X10.6版本以上。GCD是Grand Central Dispatch的缩写,是一组用于实现并发编程的C接口。GCD是基于Objective-C的Block的特性开发的,基本的业务逻辑和NSOperation很像。都是添加一个任务到一个队列,由系统来负责线程的生成和调度。因为直接使用Block,所以使用起来很是方便,降低了多线程开发的门槛。

            还是先看一下代码,和多线程系列(1)里面同一个例子,用GCD实现如下:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    	   
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [self downloadImage:IMAGE_URL];
        });
    }

            GCD的调用接口非常简单,就是将任务提交到Queue里面。

            dispatch_async函数是异步非阻塞的,调用后会立刻返回,工作由系统在线程池中分配线程去执行。有异步的当然也有同步的,dispatch_sync就是同步的阻塞的API,会一直到添加的任务完成才会返回。

            GCD实现多线程确实很简单,不需要了解多线程中的很多细节,而且效率也高。不过disaptch_queue有一些特殊的地方,实际使用中需要了解的多一些。dispatch_queue有串行运行和并行运行两种,顾名思义,串行运行就是任务顺序执行,完成一个然后执行下一个,每次只有一个任务在运行;并行运行就是各个任务可以同时运行,同时有多少任务可以并行是根据系统当时的负载决定的,这个开发者不用关心。

            系统提供了3中类型的dispatch queue:

    1. main queue

            这实际上就是主线程的队列,所以很明显,这是一个串行的queue,所有加入main queue的任务都会发动主线程运行,所以加入任务时需要注意不要加入长时间运行的任务。

    2. Global queue

            我们实际开发中最常用的队列,是并发队列。并且有high、default、low三个优先级(每个优先级都对应一个独立的queue)。通过dispatch_get_global_queue这个API可以获得queue。

    3. 自定义queue

            dispatch queue是可以自己创建的,通过dispatch_queue_create这个API来创建,dispatch_queue_create(const char *label, dispatch_queue_attr attr)这个API的第一个参数是queue的名字,要求不能重复,所以很多时候和java一样,推荐用倒写的域名,第二个参数是建立的queue的类型。这里要指出,在iOS4.3之前,只能建立串行的queue,参数就是传递DISPATCH_QUEUE_SERIAL,iOS4.3之后可以建立并行的queue了,参数是DISPATCH_QUEUE_CONCURRENT

            看到create就会牵涉到内存的管理问题,GCD的内存管理同样是用引用计数的方式,不过并不纳入iOS的内存管理,所以是需要开发者手动管理的(无论是不是ARC)。

            由于有着不同类型的队列,dispatch_async也可以嵌套使用,还是以同样的例子,我们也可以这样写:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        __block UIImage *_image;
    	   
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:IMAGE_URL]];
            _image = [[UIImage alloc] initWithData:data];
            dispatch_async(dispatch_get_main_queue(), ^{
                self.imageView.image = _image;
            });
        });
    }

            这样就在一段代码里面实现了所有的功能,包括后台下载,下载之后刷新UI,而且简单清晰。

            还有一些常用的API介绍如下:

            dispatch_get_current_queue()获取当前队列

            dispatch_queue_get_label()获取队列的名字,如果队列没有名字,返回NULL

            dispatch_set_target_queue()设定给定对象的目标队列

            dispatch_main()会阻塞主线程等待主队列main queue中的Block执行结束。


           有时我们会遇到运行一系列的任务,当任务全部结束后运行另一个特殊的任务这种场景。如果我们用dispatch_sync方法来串行运行所有的任务可以确定运行的先后顺序,但效率就会大大降低;但dispatch_async是异步非阻塞的,所以代码如下写是没用的,不能保证结束所有任务后那个特殊任务的运行时间点。

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        for(id obj in array)
            dispatch_async(queue, ^{
                [self doWork:obj];
            });
        [self doneWork];

          针对这种情况,GCD提供了dispatch group,可以将一组任务集合在一起,等待这组任务完成后再继续,上面的场景,代码应该写成下面的样子:

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_group_t group = dispatch_group_create();
        for(id obj in array)
            dispatch_group_async(group, queue, ^{
                [self doWork:obj];
            });
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        dispatch_release(group);
        [self doneWork];

            方法很简单,就是将并发的任务用dispatch_group_async异步添加到一个Group和全局队列中,dispatch_group_wait会等待这些工作完成后在返回。这样就实现了任务的顺序运行,不过dispatch_group_wait是会阻塞线程的,所以如果是主线程,这个API是不能调用的,那么我们该怎么办呢?

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_group_t group = dispatch_group_create();
        for(id obj in array)
            dispatch_group_async(group, queue, ^{
                [self doWork:obj];
            });
        dispatch_group_notify(group, queue, ^{
            [self doneWork];
        });
        dispatch_release(group);

            答案还是很简单,换一个API,使用dispatch_group_notify这个方法即可。


            有的时候我们要同步执行对数组元素的逐个操作,GCD提供了一个简单的dispatch_apply方法:

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_apply([array count], queue, ^(size_t index){
            [self doWork:obj:[array objectAtIndex:index]];
        });
        [self doneWork];


            在使用dispatch_async方法提交并行的任务时,是无法确定任务的执行顺序的,但有时我们确实需要某些工作在某个工作完成之后执行,那么可以使用Dispatch Barrier接口来实现。

        dispatch_async(queue, block1);
        dispatch_async(queue, block2);
        dispatch_barrier_async(queue, block3);
        dispatch_async(queue, block4);
        dispatch_async(queue, block5);

            dispatch_barrier_async是异步的,调用后立刻返回。这样的写法会保证block1和block2并行执行完成后才会执行block3,完成后再会并行运行block4和block5。

            请注意,这里的queue是一个并行队列,而且是自定义的那种。


            作为苹果推出的多线程的神器,GCD的内容当然远远不止这些。不过通过介绍的最最常用的这些,我们已经可以管中窥豹了。GCD针对各种不同的需求考虑的很全面,并给出了相关的解决方案。开发者使用GCD应该说是很容易的,所以真正需要关心的就变成了任务怎么划分,怎么运行,是串行还是并行等等。

            附上苹果的Grand Central Dispatch(GCD)Reference文档,需要深入了解的请参考。







  • 相关阅读:
    测试员的一天
    Python测试开发-创建模态框及保存数据
    appium ios真机自动化环境搭建&运行(送源码)
    Python测试开发-浅谈如何自动化生成测试脚本
    刷金币全自动脚本 | 让Python每天帮你薅一个早餐钱(送源码)
    Python测试开发-创建模态框及保存数据
    Python测试开发-浅谈如何自动化生成测试脚本
    appium ios真机自动化环境搭建&运行(送源码)
    刷金币全自动脚本 | 让Python每天帮你薅一个早餐钱(送源码)
    华为招进应届生8名,年薪89万-201万
  • 原文地址:https://www.cnblogs.com/huluo666/p/3645886.html
Copyright © 2011-2022 走看看