美文网首页
笔记:Objective-C高级编程 第二章 Blocks

笔记:Objective-C高级编程 第二章 Blocks

作者: andyok8 | 来源:发表于2016-09-22 06:49 被阅读0次
    t 1862021-459edd5ef79e8373.jpg

    第二章 Blocks

    2.1 Blocks 概要

    2.1.1 什么是Blocks

    Blocks是C语言的扩充功能,带有自动变量(局部变量)的匿名函数。
    C语言的函数中可能使用的变量

    • 自动变量(局部变量)
    • 函数的参数
    • 静态变量(静态局部变量)
    • 静态全局变量
    • 全局变量
      其中,在函数的多次调用之间能够传递值的变量有:
    • 静态变量(静态局部变量)
    • 静态全局变量
    • 全局变量

    2.2 Blocks 模式

    2.2.1 Blocks语法

    完整形式的Block语法与一般的C语言函数定义相比,仅有二点不同:

    • 没有函数名
    • 带有^(插入记号 caret)

    完整形式:
    ^int (int count){ return count+1;}
    省略返回值类型
    ^(int count){ return count+1;}
    省略返回值和参数类型
    ^{printf("Block");}

    声明Block类型变量的示例:
    int (^blk) (int);
    Block类型变量的用途与C语言的变量作用完全相同

    Block赋值:
    int (^blk) (int) = ^(int count){return count+1;};
    int (^blk1) (int) = blk;
    int (^blk2) (int);
    blk2 = blk1;
    在函数参数中使用Block类型量可以向函数传递Block:
    void func(int (^blk) (int))
    {
    }
    在函数返回值中指定Block类型,可以将Block作为函数的返回值返回:

    在函数参数和返回值中使用Block类型变量时 记述方式极为复杂,可以用命typedef来解决问题:
    typedef int (^blk_t) (int);

    Block的指针类型变量:
    typedef int (^blk_t)(int);
    blk_t blk = ^(int count) {return count+1;};
    blk_t *blkptr = &blk;
    (*blkptr)(10);

    2.2.3 截获自动变量值

    "带有自动变量值"在Blocks中表现为“截获自动变量值”,即保存该自动变量的瞬间值。

    2.2.4 __block说明符

    若想在Block语法的表达式中将值赋给在Block语法外声明的自动变量,需要在该自动变量上附加__block说明符
    __block int val = 0; //该变量称为__block变量

    2.2.5 截获的自动变量

    int val = 0;
    void (^blk) (void) = ^{val = 1;};//会产生编译错误

    截获Objective-C对象:
    id array = [[NSMutableArray alloc] init];
    void (^blk)(void) = ^{
    id obj = [[NSObject alloc] init];
    [array addObject:obj];//不会报错
    array = [[NSMutableArray alloc] init];//报错
    }
    在使用C语言数组时必须小心使用其指针:
    const char text[] = "hello";//区别
    void (^block)(void) = ^{
    printf("%c\n",text[2]);//报错
    };

    const char *text = "hello";//区别
    void (^block)(void) = ^{
    printf("%c\n",text[2]);//不会报错
    };

    2.3 Block的实现

    2.3.1 Block的实质

    所谓Block就是Objective_C对象
    struct __main_block_impl_0 *__cself;
    参数__cself是__main_block_impl_0结构体的指针
    objc_object与obj_class结构体相同
    objc_object与obj_class结构体归根结底是在各个对象和类的实现中使用的最基本的结构体
    struct __main_block_impl_0{
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    };
    struct __block_impl{
    void *isa;
    int Flags;//标志
    int Reserved;//今后版本升级所需的区域以及函数指针
    void *FuncPtr;;//函数指针
    };
    struct __main_block_desc_0{
    unsigned long reserved;
    unsigned long Block_size;
    };
    typedef struct objc_object{
    Class isa;
    }*id;
    typedef struct objc_class *Class;
    struct objc_class{
    Class isa;
    };
    struct class_t{
    struct class_t *isa;
    struct class_t *superclass;
    Cache cache;
    IMP *vtable;
    uintptr_t date_NEVER_USE;
    };
    各类的结构体就是基于objc_class结构体的class_t结构体。

    ^{printf("Block")};
    static void __main_block_func_0(struct __main_block_impl_0 *__cself)
    {
    printf("Block");
    }
    根据Block语法所属的函数名(此处为main)和该Block语法在该函数出现的顺序值(此处为0)来给经clang变换的函数换名。

    2.3.2 截获自动变量值

    Block语法表达式中使用的自动变量被作为成员变量追加到了__main_block_impl_0结构体中
    Blocks的自动变量截获只针对Block中使用的自动变量
    Block语法表达式所使用的自动变量值被保存到Block的结构体实例中

    2.3.3 __block存储域类说明符

    C语言存储域类说明符

    • typedef
    • extern
    • static//表示作为静态变量存储在数据区中
    • auto //表示作为自动变量存储在栈中
    • register
      struct __Block_byref_val_0{
      void *__isa;
      __Block_byref_val_0 *__forwarding;
      int __flags;
      int __size;
      int val;
      }

    __block变量也同Block一样变成_Block_byref_val_0结构体类型的自动变量,即栈上生成的__Block_byref_val_0结构体实例

    在Block中向静态变量赋值时 使用了指向该静态变量的指针。而向__block变量赋值要比这个更为复杂。Block的__main_block_impl_0结构体实例持有指向_block变量的_Block_byref_val_0结构体实例的指针。
    _Block_byref_val_0结构体实例的成员变量_forwarding持有指向该实例自身的指针,通过成员变量__forwarding访问成员变量val
    __block变量的_Block_byref_val_0结构体并不在Block用__main_block_impl_0结构体中 而是使用__Block_byref_val_0结构体实例的指针,这样做是为了在多个Block中使用__block变量

    2.3.4 Block存储域

    Block与__block变量的实质

    名称 实质
    Block 栈上Block的结构体实例
    __block变量 栈上__block变量的结构体实例

    Block的类

    设置对象的存储域
    _NSConcreteStackBlock
    _NSConcreteGlobalBlock 程序的数据区域(.data区)
    _NSConcreteMallocBlock

    另还有一个程序区域(.text区)

    在以下情况下,Block为_NSConcreteGlobalBlock类对象

    • 记述全局变量的地方有Block语法时
    • Block语法的表达式中不使用截获的自动变量
      除此之外的Block语法生成的Block为_NSConcreteStackBlock类对象 且设置在栈上。

    Block超出变量作用域可存在的原因:
    Block提供了将Block和__block变量从栈上复制到堆上的方法来解决这个问题。

    编译器不能进行判断 将Block从栈上复制到堆上, 所以需使用copy方法:

    • 向方法或函数的参数中传递Block时

    编译器可以自行判断 自动生成将Block从栈上复制到堆上的代码:

    • Block作为函数返回值时
    • Cocoa框架的方法且方法名中含有usingBlock等时
    • Grand Central Dispatch的API
      注:从栈上复制到堆上是相当消耗CPU的

    2.3.5 __block变量存储域

    __block变量用结构体成员变量__forwarding存在的原因:
    通过Block的复制__block变量也从栈复制到堆,此时可同时访问栈上的__block变量和堆上的__block变量。
    栈上的__block变量用结构体实例在__block变量从栈复制到堆上时,会将成员变量__forwarding的值替换为复制目标堆上的__block变量用结构体实例的地址。

    2.3.6 截获对象

    C语言结构体不能含有附有__strong修饰符的变量,因为编译器不知道应何时进行C语言结构体的初始化和废弃操作 不能很好地管理内存。
    但是Object_C的运行库能够准确把握Block从栈复制到堆以及堆上的Block被废弃的时机。
    什么时候栈上的Block会复制到堆呢?

    • 调用Block的copy实例方法
    • Block作为函数返回值返回时
    • 将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
    • 在方法名中含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中传递Block时

    Block中使用对象类型自动变量时,除以下情形外,推荐调用Block的copy实例方法:

    • Block作为函数返回值返回时
    • 将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
    • 在方法名中含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中传递Block时

    2.3.7 __block变量和对象

    没有设定__autoreleasing修饰符与Block同时使用的方法:
    __block id __autoreleasing obj = [[NSObject alloc] init];//编译器报错

    2.3.7 循环引用

    避免循环引用二种方式:

    • 使用__block
    • 使用__weak修饰符及__unsafe_unretained修饰符

    使用_block变量的优点:

    • 通过__block变量可控制对象的持有期间
    • 在不能使用__weak修饰符的环境中可以不需使用__unsafe_unretained修饰符即可(不必担心悬垂指针)
    • 在执行Block时可动态地决定是否将nil或其它对象赋值在__block变量中

    使用__block变量的缺点如下:

    • 为了避免循环引用必须执行Block

    存在执行了Block语法 却不执行Block的路径时,无法避免循环引用.

    相关文章

      网友评论

          本文标题:笔记:Objective-C高级编程 第二章 Blocks

          本文链接:https://www.haomeiwen.com/subject/qbqwettx.html