iOS中block变量捕获原理详析

2020-01-21 03:17:39于海丽

Block概述

Block它是C语言级别和运行时方面的一个特征。Block封装了一段代码逻辑,也用{}括起,和标准C语言中的函数/函数指针很相似,此外就是blokc能够对定义环境中的变量可以引用到。这一点和其它各种语言中所说的“闭包”是非常类似的概念。在iOS中,block有很多应用场景,比如对代码封装作为参数传递。这在使用dispatch并发(Operation中也有BlockOperation)和completion异步回调等处都广泛应用。

Block是苹果官方特别推荐使用的数据类型,使用场景比较广泛 动画 多线程 集合遍历 网络请求回调 Block的作用 用来保存某一段代码,可以在恰当时候再去出来调用 功能类似于函数和方法

block对变量的捕获

1:可以捕获不可以修改变量

局部变量

2:可以捕获且可以修改变量

全局变量 静态变量 __block修饰的局部变量

原理分析:

1. 局部变量为什么可以被捕获确不能修改


int a = 10;
void (^blcok)() = [^{
 NSLog(@"%d",a);
} copy];
a=20;
blcok(); // log : a = 10

结果应该大家都知道,但是为什么会这样呢?

我们用clang转化之后看看

ios,block,变量,定义block变量,成员变量

从block定义来看


void (*blcok)() = (void (*)())((id (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)())&__ZMX__blockTest_block_impl_0((void *)__ZMX__blockTest_block_func_0, &__ZMX__blockTest_block_desc_0_DATA, a)), sel_registerName("copy")); 

block的实现是通过__ZMX__blockTest_block_impl_0结构体的构造方法来定义的,我们来看下这个结构体


struct __ZMX__blockTest_block_impl_0 {
 struct __block_impl impl;
 struct __ZMX__blockTest_block_desc_0* Desc;
 int a;
 __ZMX__blockTest_block_impl_0(void *fp, struct __ZMX__blockTest_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
 impl.isa = &_NSConcreteStackBlock;
 impl.Flags = flags;
 impl.FuncPtr = fp;
 Desc = desc;
 }
};

impt:


struct __block_impl {
 void *isa;
 int Flags;
 int Reserved;
 void *FuncPtr;
};

isa:指向Class的指针

flags:一些标识

reserced:保留的一些变量

funcptr:函数指针

__ZMX__blockTest_block_desc_0:


static struct __ZMX__blockTest_block_desc_0 {
 size_t reserved;
 size_t Block_size;
} __ZMX__blockTest_block_desc_0_DATA = { 0, sizeof(struct __ZMX__blockTest_block_impl_0)};

reserced:保留的一些变量

size:内存大小

__ZMX__blockTest_block_impl_0 构造方法

我们可以看到这个构造方法有四个参数