zoukankan      html  css  js  c++  java
  • 2.2 更易用的多线程——GCD

    在iOS 4.0之后,GCD开始以飞快的普及率出现在众多开发者的代码中。由于它提供了更易于使用的并发模型,而不仅仅是上面讲到的线程锁,这就使得很多线程处理的陷阱得以有效避免,同时在libdispatch库中,苹果提供了大量的API,大大降低了程序员的使用难度,以及提高功能的健壮性,从而很少开发人员的青睐。

    2.2.1 GCD是什么?

    谈到GCD(Grand Central Dispatch)很多开发者都不陌生,这是一个与block语法块结合十分紧密的多线程技术,它拥有丰富的语言特性,多样的库供开发者使用,对于硬件上多核处理器提供了广泛地系统级别的优化,在多核的iOS和OS X硬件体系中,高并发执行能力得到有效地提升。同时它提供了一种抽象的线程,而这种抽象是基于“调度队列”(dispatch queue)。开发者不需要关心线程中具体实现了哪些细节,因为系统框架已经将block语法块加入到队列中,由GCD统一负责处理所有事务。

    GCD是基于block语法块与c库函数的技术。如果想熟练使用GCD必须首先对block有充分的了解,不然很容易造成很多问题,比如本文之前提到的内存的引用环,这在如果线程中发生这种问题是十分致命的,因为开发者很难掌控子线程的生命周期,或者说,开发者并不能准确地确定线程中地代码何时能执行完毕,这就会到这大量内存泄漏。所以在学习GCD前,首先了解block这个至关重要。

    2.2.2  什么是Block

    Block语法块这个概念由来已久,也并非只有objective-c的语法独有的,这个语言的特性是作为延展加入到GCC编译器中的。从技术层面来讲block更接近与c语言层面的特性。在Mac OS X 10.6以及iOS4.0之后的版本之后支持block语法。在java,或是近来比较火热的swift语法中,block对象会被称之为“闭包”(closure)。而在oc中,开发者更习惯将他叫做“语法块”,或者直接称之为“块”。

    Block的语法特点:

    1. 如同c语言函数一样,拥有参数以及参数类型,同时拥有返回值与返回值类型。
    2. 加入到块中的语法将会被块强引用,并持有,直到块被销毁。
    3. 可以捕捉它所定义的作用域范围的状态,并传递到其他作用域。

    使用块的场景有很多,比如作为传统的函数回调,以及访问所需要的作用域内的局部变量,而不是需要使用一个开发者想要执行操作时集成“上下文”(context)的数据来进行回调。

    2.2.3  Block基础知识

    Block语法和标准c语言函数极为类似,但不同的是,Block语法块的作用域处于定义它的函数之中。如下代码。

     /**
         *  构建一个加法运算的block语法块
         *
         *  @param firstNum  输入第一个字符
         *  @param secondNum 输入第二个字符
         *
         *  @return 返回两个数的和
         */
        int (^sumNumbers)(int firstNum,int secondNum) = ^(int firstNum,int secondNum){
            return firstNum+secondNum;
        };
        /**
         *  调用block语法
         *  计算10+13之和
         */
        NSLog(@"sumNumbers = %d",sumNumbers(10,13));
    打印结果:2015-06-23 16:59:06.511 iOS性能优化[54212:628205] sumNumbers = 23

          如果在Block作用域之外的一个变量需要在Block块中修改自身的值,就需要用到__block这个修饰符了,如下代码。

     /**
         *  定义一个外部变量
         */
        int __block multNumbers = 0;
        /**
         *  构建一个加法运算的block语法块
         *
         *  @param firstNum  输入第一个字符
         *  @param secondNum 输入第二个字符
         *
         *  @return 返回两个数的和
         */
        int (^sumNumbers)(int firstNum,int secondNum) = ^(int firstNum,int secondNum){
            multNumbers = firstNum*secondNum;
            return firstNum+secondNum;
        };
        /**
         *  调用block语法
         *  计算10+13之和
         *  输出修改后的multNumbers的值
         */
        NSLog(@"sumNumbers = %d , multNumbers = %d",sumNumbers(10,13),multNumbers);

    如果不加这个修饰符的话,xcode则会抛出这样的错误,如图2.4。

    图2.4 不使用修饰符__block xcode抛出错误

    此外Block语法还可以通过外部声明,在不同的地方是使用同一个命名的block,这种方法极为常用,而且十分灵活,如下代码。

    enum CountNumbersType
    {
        CountNumbersTypeSum,               //加法
        CountNumbersTypeSub,               //减法
        CountNumbersTypeMult,              //乘法
        CountNumbersTypeDivision           //除法
    };
    //定义枚举类型
    typedef enum CountNumbersType CountNumbersType;
    /**
     *   声明计算block
     *
     *  @return 返回计算结果
     */
    typedef int(^CountNumbers)(int,int,CountNumbersType);
    {
       /**
         *  定义block内方法
         *
         *  @param firstNum  第一个参数数字
         *  @param secondNum 第二个参数数字
         *  @param type      运算类型枚举值
         *
         *  @return 运算返回值
         */
        CountNumbers countNumbers = ^(int firstNum,int secondNum,CountNumbersType type){
            switch (type) {
                case CountNumbersTypeSum:
                    return firstNum + secondNum;
                    break;
                case CountNumbersTypeSub:
                    return firstNum - secondNum;
                    break;
                case CountNumbersTypeMult:
                    return firstNum * secondNum;
                    break;
                case CountNumbersTypeDivision:
                    return firstNum / secondNum;
                    break;
                default:
                    return 0;
                    break;
            }
        };
        /**
         *  打印输出结果
         *
         *  @param sum                  相加结果
         *  @param sub                  相减结果
         *  @param mult                 相乘结果
         *  @param division             相除结果
         *
         *  @return 打印输出结果
         */
        NSLog(@"sum = %d,sub = %d,mult = %d,division = %d.",
              countNumbers(10,5,CountNumbersTypeSum),
              countNumbers(10,5,CountNumbersTypeSub),
              countNumbers(10,5,CountNumbersTypeMult),
              countNumbers(10,5,CountNumbersTypeDivision));
         }
    打印结果:2015-06-23 18:19:12.016 iOS性能优化[58960:679603] sum = 15,sub = 5,mult = 50,division = 2.

    在很多情况下,block对象还可以被当作参数进行传递,下面要讲的GCD就是这样做的,还有AFNetWork 2.0使用的参数也是block回调,当然开发者也可以自己写这样的方法,如下模拟网络请求代码。

    通过上面代码可以看出,使用block作为回调函数,使用起来十分简便,但使用block一定需要注意的是:

    避免内存管理上面的循环引用。

    注意block的异步性,这里提到的异步性和多线程的异步并不相同,而是代码执行方面,block块中的代码执行于它所定义的代码区域并非顺序执行,这点尤为需要注意,否则会造成问题。

  • 相关阅读:
    Cordova 混合开发
    可能比文档还详细--VueRouter完全指北
    VUE项目实现页面跳转
    Vuex结合 async/await 优雅的管理接口请求
    vuex深入理解 modules
    vuex学习与实践——mapState、getter、mapGetters
    vuex里mapState,mapGetters使用详解
    vue.js相关UI组件收集
    vuex最简单、最直白、最全的入门文档
    深入理解表单脚本系列第三篇——选择文本
  • 原文地址:https://www.cnblogs.com/xuruofan/p/5775448.html
Copyright © 2011-2022 走看看