iOS Block

Posted xiaoxiaobukuang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS Block相关的知识,希望对你有一定的参考价值。

Block Block介绍 解获变量 __block修饰词 Block的内存管理 Block的循环引用

一、什么是Block

  • Block是将函数及其执行上下文封装起来的对象。
  • Block调用即是函数的调用

    int multiplier = 6;io
    int(^Block)(int) = ^int(int num) 
        return num*multiplier;
    ;
    Block(2);

二、Block的本质

我们使用源码解析

  • 使用【clang -rewrite-objc file.m】查看编译之后的文件内容;

文件XYBlock.m

- (void)method 
    int multiplier = 6;
    int(^Block)(int) = ^int(int num) 
        return num*multiplier;
    ;
    Block(2);

打开终端,进入XYBlock.m所在的目录下,输入:

clang -rewrite-objc XYBlock.m

会在XYBlock.m的目录下生成XYBlock.cpp文件,打开该文件后解析如下:

结构体__XYBlock__method_block_impl_0

struct __XYBlock__method_block_impl_0 
  struct __block_impl impl;
  struct __XYBlock__method_block_desc_0* Desc;
  int multiplier;
  __XYBlock__method_block_impl_0(void *fp, struct __XYBlock__method_block_desc_0 *desc, int _multiplier, int flags=0) : multiplier(_multiplier) 
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  
;

结构体__block_impl

struct __block_impl 
  void *isa;//isa指针,Block是对象的标志
  int Flags;
  int Reserved;
  void *FuncPtr;//无类型的函数指针
;

函数__XYBlock__method_block_func_0

static int __XYBlock__method_block_func_0(struct __XYBlock__method_block_impl_0 *__cself, int num) 
  int multiplier = __cself->multiplier; // bound by copy

        return num*multiplier;
    

三、截获变量

- (void)method 
    int multiplier = 6;
    int(^Block)(int) = ^int(int num) 
        return num*multiplier;
    ;
    multiplier = 4;
    NSLog(@"result is %d",Block(2));

打印台:

result is 12
  • 局部变量
    1.基本数据类型
    2.对象类型
  • 静态局部变量
  • 全局变量
  • 静态全局变量

(1)、对于基本数据类型的局部变量截获其值。
(2)、对于对象类型的局部变量连同所有权修饰符一起截获。
(3)、对于局部静态变量以指针形式截获。
(4)、对于全局变量、静态全局变量不截获

变量是否捕获到block内部访问法方式
基本数据类型的局部变量值传递
对象数据类型的局部变量连同所有权修饰符截获
局部静态变量指针传递
全局变量直接传递
静态全局变量直接传递

源码解析

  • 使用【clang -rewrite-objc -fobjc-arc file.m】命令

文件XYBlock.m如下:

通过编译生成XYBlock.cpp文件内容如下:

结构体__XYBlock__method_block_impl_0

struct __XYBlock__method_block_impl_0 
  struct __block_impl impl;
  struct __XYBlock__method_block_desc_0* Desc;
  int var;
  __unsafe_unretained id unsafe_obj;
  __strong id strong_obj;
  int *static_var;
  __XYBlock__method_block_impl_0(void *fp, struct __XYBlock__method_block_desc_0 *desc, int _var, __unsafe_unretained id _unsafe_obj, __strong id _strong_obj, int *_static_var, int flags=0) : var(_var), unsafe_obj(_unsafe_obj), strong_obj(_strong_obj), static_var(_static_var) 
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  
;

函数__XYBlock__method_block_func_0

static void __XYBlock__method_block_func_0(struct __XYBlock__method_block_impl_0 *__cself) 
  int var = __cself->var; // bound by copy
  __unsafe_unretained id unsafe_obj = __cself->unsafe_obj; // bound by copy
  __strong id strong_obj = __cself->strong_obj; // bound by copy
  int *static_var = __cself->static_var; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_1271c0_mi_0, var);

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_1271c0_mi_1, unsafe_obj);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_1271c0_mi_2, strong_obj);

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_1271c0_mi_3, (*static_var));

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_1271c0_mi_4, global_var);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_1271c0_mi_5, static_global_var);
    

例子:文件XYBlock.m

// 全局变量
int global_var = 4;
// 静态全局变量
static int static_global_var = 5;

- (void)method 
    // 基本数据类型的局部变量
    int var = 1;
    // 对象类型的局部变量
    __unsafe_unretained id unsafe_obj = nil;
    __strong id strong_obj = nil;
    
    // 静态局部变量
    static int static_var = 3;
    
    void(^Block)(void) = ^
        NSLog(@"局部变量<基本数据类型> var %d", var);
        
        NSLog(@"局部变量<__unsafe_unretained 对象类型> var %@", unsafe_obj);
        NSLog(@"局部变量<__strong 对象类型> var %@", strong_obj);
        
        NSLog(@"静态局部变量 %d", static_var);
        
        NSLog(@"全局变量 %d", global_var);
        NSLog(@"静态全局变量 %d", static_global_var);
    ;
    var = 99;
    unsafe_obj = @"99";
    strong_obj = @"99";
    static_var = 99;
    global_var = 99;
    static_global_var = 99;

    NSLog(@"局部变量<基本数据类型> var %d", var);

    NSLog(@"局部变量<__unsafe_unretained 对象类型> var %@", unsafe_obj);
    NSLog(@"局部变量<__strong 对象类型> var %@", strong_obj);

    NSLog(@"静态局部变量 %d", static_var);

    NSLog(@"全局变量 %d", global_var);
    NSLog(@"静态全局变量 %d", static_global_var);

    NSLog(@"——————————————————————————————————————————");
    Block();

控制台打印:

局部变量<基本数据类型> var 99
局部变量<__unsafe_unretained 对象类型> var 99
局部变量<__strong 对象类型> var 99
静态局部变量 99
全局变量 99
静态全局变量 99
————————————————————————————————
局部变量<基本数据类型> var 1
局部变量<__unsafe_unretained 对象类型> var (null)
局部变量<__strong 对象类型> var (null)
静态局部变量 99
全局变量 99
静态全局变量 99

四、__block修饰符

  • 一般情况下,对被截获变量进行赋值操作需添加__block修饰符

注意:

笔试题1:

笔试题2:

1、对变量进行赋值时

需要__block修饰符:
(1)、局部变量的基本数据类型
(2)、局部变量的对象类型
不需要__block修饰符:
(1)、静态局部变量
(2)、全局变量
(3)、静态全局变量

2、__block修饰的变量变成了对象

- (void)method 
    __block int multiplier = 6;
    int(^Block)(int) = ^int(int num) 
       return num*multiplier;
    ;
    multiplier = 4;
    NSLog(@"result is %d",Block(2));

打印台:

result is 8

通过编译生成XYBlock.cpp文件内容如下:

static void _I_XYBlock_method(XYBlock * self, SEL _cmd) 
    __attribute__((__blocks__(byref))) __Block_byref_multiplier_0 multiplier = (void*)0,(__Block_byref_multiplier_0 *)&multiplier, 0, sizeof(__Block_byref_multiplier_0), 6;
    int(*Block)(int) = ((int (*)(int))&__XYBlock__method_block_impl_0((void *)__XYBlock__method_block_func_0, &__XYBlock__method_block_desc_0_DATA, (__Block_byref_multiplier_0 *)&multiplier, 570425344));
    (multiplier.__forwarding->multiplier) = 4;
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_4b696f_mi_0,((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block, 2));

结构体__Block_byref_multiplier_0

struct __Block_byref_multiplier_0 
  void *__isa;
__Block_byref_multiplier_0 *__forwarding;
 int __flags;
 int __size;
 int multiplier;
;


注意:
对于栈上__forwarding指向的是_block变量本身

五、Block的内存管理

1、类型

block有三种类型

  • NSGlobalBlock ( _NSConcreteGlobalBlock )全局类型的block
  • NSStackBlock ( _NSConcreteStackBlock )栈类型的block
  • NSMallocBlock ( _NSConcreteMallocBlock )堆类型的block

2、内存分配

3、Block的Copy操作

Block的类别源的配置存储域Copy效果
_NSConcreteGlobalBlock程序的数据区域什么也不做
_NSConcreteStackBlock从栈复制到堆
_NSConcreteMallocBlock引用计数增加

4、栈上的Block的销毁

5、栈上Block的Copy

6、栈上__block变量的Copy

7、__forwarding总结

  • 不论在任何内存位置,都可以顺利的访问同一个__block变量;

六、Block的循环引用

1、__weak消除循环引用

2、__block消除循环引用

  • 在MRC下,不会产生循环引用。
  • 在ARC下,会产生循环引用,引用内存泄露。

ARC下的引用循环:


解决方案:

  • 通过__block变量可控制对象的持有期间;
  • 在执行Block时可动态地决定是否将nil或其他对象赋值在__block变量中;
  • 为避免循环引用必须执行Block;

以上是关于iOS Block的主要内容,如果未能解决你的问题,请参考以下文章

iOS中block的使用详解

iOS开发系列-Block

iOS开发中将block作为参数、属性使用

iOS底层探索之Block——Block的本质

iOS开发面试拿offer攻略之block篇

看懂iOS代码块Block