用了Block那么久,一直以来没有好好整理,只知道如何使用,对有些概念也不是了解特别清楚,感觉似懂非懂的。借助网上各位前辈的教程,从新疏导一遍,以方便以后自己又犯晕了可以查看。
Block的书写形式
Block变量的声明格式为: 返回值类型(^Block名字)(参数列表);
Block变量的赋值格式为: Block变量 = ^(参数列表){函数体};
对于新手,推荐:使用typedef定义Block类型,因为这种方式可以提高代码的可读性,不至于犯晕。
Block内存形式
我们先来看看一个由C/C++/OBJC编译的程序占用内存分布的结构:
其实,block有三种类型:
* 全局块(_NSConcreteGlobalBlock):存在于全局内存中, 相当于单例.
* 栈块(_NSConcreteStackBlock):存在于栈内存中, 超出其作用域则马上被销毁
* 堆块(_NSConcreteMallocBlock):存在于堆内存中, 是一个带引用计数的对象, 需要自行管理其内存
遇到一个Block,我们怎么这个Block的存储位置呢?
(1)Block不访问外界变量(包括栈中和堆中的变量)
Block 既不在栈又不在堆中,在代码段中,ARC和MRC下都是如此。此时为全局块。
(2)Block访问外界变量
MRC 环境下:访问外界变量的 Block 默认存储栈中。
ARC 环境下:访问外界变量的 Block 默认存储在堆中(实际是放在栈区,然后ARC情况下自动又拷贝到堆区),自动释放
block截获变量的类型
block可以中的外部变量一共有五种,依次是全局变量、全局静态变量、局部静态变量、_block修饰的变量和一个局部变量(截获后为const类型)。
ARC 对 block 类型的影响
* 在 ARC下,将只会有 NSConcreteGlobalBlock NSConcreteMallocBlock 类型的 block。
* 原本的 NSConcreteStackBlock 的 block 会被 NSConcreteMallocBlock 类型的 block 替代。
* 针对不同block类型的copy、retain、release操作
对block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;
* 针对NSConcreteGlobalBlock:retain、copy、release操作都无效;
* 针对NSConcreteStackBlock:retain、release操作无效
注意的是,NSConcreteStackBlock离开其作用域后,该block内存将被回收,即使retain也没用。容易犯的错误是 [[mutableAarry addObject:stackBlock],在stackBlock离开其作用域失效后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[stackBlock copy]]。NSConcreteMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;尽量不要对block使用retain操作。因为从上可以看出,retain操作对NSConcreteStackBlock并没有效果,这样会误以为retain生效了,在后续调用block的时候,其实block早就被释放了,从而导致crash
[参考]
iOS Block 详解
iOS block,你要看的这都有
iOS 中 block 的循环引用问题
iOS 中的 block 是如何持有对象的
深入分析 Objective-C block、weakself、strongself 实现原理
__block与__weak的真正区别
一篇文章看懂 iOS 代码块 Block 字数
谈Objective-C block的实现
神奇的 BlocksKit