前言
在了解了Block
的基础知识后,希望对它有更多的认识比如:
Block
里的野指针
错误为什么会出现?怎样避免?Block
里的循环引用
的原理是什么?Block
在ARC
与MRC
下有什么不同?
在看iOS与OS X多线程和内存管理时,看到block
那章,我就觉得看不懂了。这一章包含太多的底层源码,非常枯燥而且不好理解,因此难以集中精神。
因此在我对之的理解稍微清晰了一些时,我就记录了下来。
将环境改为
MRC
:打开XCode->点击工程->Build Phases->Compile Sources->点击要配置MRC环境的文件->添上-fno-objc-arc
。若要改回ARC
, 则将-fno-objc-arc
改成-fobjc-arc
.
Block的种类
NSStackBlock
- 引用了外部变量的
block
MRC
下,NSStackBlock
位于栈
中,block
函数体返回后,其位于的栈段会被清除,即block
会无效,此时如果再次访问block
,就会造成野指针
错误。解决方法
是:通过对block做copy
操作,使之转换为位于堆
中的NSMallocBlock
。ARC
下,与MRC
不同的是,不需要再做copy
操作,在ARC
下,所有的block都会自动转到堆区,即NSMallocBlock。
e.g.
1 | typedef void (^myBlock)(); |
NSMallocBlock
- 由
NSStackBlock copy
得来,位于内存的堆
区。
NSGlobalStack
- 没有引用外部变量的
block
就是NSGlobalStack
。 - 既不位于
堆
,也不位于栈
,是程序段
。
e.g.
1 | int (^sum)(int, int) = ^(int x, int y) { |
野指针错误
1 | typedef void (^block)(); |
arc_getBlock()
最后返回的是block_copy
就不会出现野指针错误,而返回myBlock
就会出现野指针错误,因为myBlock
在栈上,返回后,就被清理了。
按理说是这样,但我做测试的时候,发现两者都可以。
- 全局和静态变量在内存中的位置是确定的,block copy时不会retain这些对象。
- 局部变量在block copy时,会被retain,增加其引用计数。
- 实例变量或属性在block copy时不是直接retain这个对象本身,而是retain self。
下面来做一个稍大的测试以便更好地理解e.g
最后,这里有一个从
OneVCat
的文中看到的关于block的quiz,有兴趣可以做做。