首先复习一下之前写过的关于 block 的内容
1>>> block 的定义及格式
就拿无返回值 有参数举个例子算了
typedef void(^MyBlock)(NSString * str)
2>>> block 的几种类型(三种)
_NSConcreteGlobalBlock:全局的静态 block,类似函数。如果block里不获取任何外部变量。或者的变量是全局作用域时,如成员变量、属性等; 这个时候就是Global类型
_NSConcreteStackBlock:保存在栈中的 block,栈都是由系统管理内存,当函数返回时会被销毁。__block类型的变量也同样被销毁。为了不被销毁,block会将block和__block变量从栈拷贝到堆。
_NSConcreteMallocBlock:保存在堆中的 block,堆内存可以由开发人员来控制。当引用计数为 0 时会被销毁。
所以声明一个 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 修饰的依然在栈上