深刻了解 ios Block 的内部结构

首先复习一下以前写过的关于 block 的内容网络

1>>> block 的定义及格式闭包

就拿无返回值 有参数举个例子算了函数

typedef void(^MyBlock)(NSString * str)翻译

2>>> block 的几种类型(三种)代理

_NSConcreteGlobalBlock:全局的静态 block,相似函数。若是block里不获取任何外部变量。或者的变量是全局做用域时,如成员变量、属性等; 这个时候就是Global类型指针

_NSConcreteStackBlock:保存在栈中的 block,栈都是由系统管理内存,当函数返回时会被销毁。__block类型的变量也一样被销毁。为了避免被销毁,block会将block和__block变量从栈拷贝到堆。对象

_NSConcreteMallocBlock:保存在堆中的 block,堆内存能够由开发人员来控制。当引用计数为 0 时会被销毁。blog

因此声明一个 block属性时记得用 copy 来修饰内存

3>>> block 的做用作用域

其实就是匿名函数, 闭包, 或者 js那种回调函数, 能够代替代理模式那种复杂的步骤, 一个最经常使用的用法, 好比网络请求获取内容, 先定义一个 block 在你请求完成以后, 调用 block, 那么当你调用请求方法的时候, 就能够在 block 里作解析收到网络请求内容的操做了, 就像 callback, 或者用来反向传值也都是比较方便的, 或者用来捕获当前做用域的变量

4>>> block 须要注意的问题

当在 block 中用到局部变量时, 全局变量能够修改, 局部变量不能修改他的值, 若是必定要修改, 请用__  block 修饰, 问起缘由, 后面会作详细解析, __block 修饰的 block 变量其实就是一个结构体

另外一个问题就是循环引用, 例如当前类持有一个 block 的属性, 而后在 block 的实现里又引用了当前类, 就会致使循环引用, 当前对象不能释放 ,内存泄漏, 解决办法就 weak strong dance, 在 block 外部先用 weak 修饰 在 block 内部再用 strong 修饰

----------------------------------******************-----------------------------------

新建一个 testBlock.m 文件 , 运行 clang -rewrite-objc testBlock.m 能够翻译成. cpp的 C++源码

// 声明一个 blockName的 block  输出打印 block 函数 而后执行这个 block

翻译后的

翻译成源码后有不少文件 主要的文件就这四个

 

__main_block_func_0    //这是block要执行的函数, 也就是 block 块内的内容

 

_main_block_desc_0 // 这是block的描述信息的结构体 对于咱们来讲没什么做用

 

_main_block_impl_0 // 这就是 block 实现部分的入口 这里能够看出一些赋值 和 block 的类型

 

_block_impl  这个就是主角了 这是 block 的真正结构-结构体  含有 isa 指针, 这也是为何不少人说 block 能够看作对象的缘由 , 所以 block 也能够赋值为 nil

FuncPtr 能够在 int main 中看到 blockName -> FuncPtr 指向了函数的实现

这个是没有在 block 内部获取局部变量的  下面看看 block 内部是如何获取局部变量的

--------------------------------------**************--------------------------------

咱们在上面的 block 外面声明一个 int num = 10 的局部变量  而后看他的 cpp 实现 发现有什么不一样, 多了一个 num变量, 这里的__ cself 就至关于 OC 的 self, 在 block 定义的时候就已经把这个 num的变量值拷贝过来了 , 即至关于拷贝了 num 的值进 block 内部, 至关于一个快照, 与外部的 num 变量没有关系了, 因此无法修改 num 的值

 

再看看__block 修饰变量的时候

这就发生了巨大的变化 多了一个__ Block_byref_num_0 的变量, 这个是什么呢? 上面就能够看到这个一个结构体, 里面有个 num的外部变量, 还有 forwarding的指向堆上的指针

也就是说 加上__ block int num 以后变量就变成了 __Block_byref_num_0的指针, 也便是 num经过这个指针传递给了 block, 而不是 block 只捕获了他的值, 因此block 内部改变变量的值就变成了 在block要执行的函数 __main_block_func_0中,咱们经过__Block_byref_num_0的__forwarding指针来修改的 外部变量,即:(num->__forwarding->num) = 10;

就是修改 forwarding 指向的内存的值 

 

//再啰嗦一点, block 不copy 的话, 是声明在栈上的 像 int a = 10同样, copy 以后 block 在堆上 , 同时__ block 修饰的 block 变量也会 copy 一份指针变量到堆上, 而没有使用__ block 修饰的依然在栈上

相关文章
相关标签/搜索