首先复习一下以前写过的关于 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 修饰的依然在栈上