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文件,打开该文件后解析如下:
Block

结构体__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

iOS block从零开始

李洪强iOS开发之Block和协议