前言
在了解了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,有兴趣可以做做。