复习下block和内存相关的知识
Stack(栈) & Heap(堆)
栈的读写速度比堆快,这就解释了为什么方法中的变量会分配到栈空间。
一般而言,代码中被{}
包含的区间变量都存储在栈中,当这一区间执行结束的时候,所有区间变量都会被系统自动释放,所以栈中的对象无法被外界retain
。
堆用来存储对象Object
,我们需要自己释放堆中的对象(ARC只是让编译器帮我们做了而已)。
内存泄漏发生在堆上,当堆上分配的内存空间没有被显示释放的时候,泄漏就发生了。
不同的应用有不同的堆空间,不同的线程有不同的栈,同一应用中的不同线程通过堆来共享数据。
Block
Block是唯一分配在栈上的对象。
栈中存储的内容一般是一些定长的变量,比如int
,bool
,对象地址等。Block也是确定长度的,一个Block被创建之后就无法再修改。
当Block所在的区间执行结束,它就会被释放,我们知道栈中的对象无法被retain
,那么怎么传递Block呢?
答案是将Block拷贝到堆上。
这就是为什么我们经常用copy
来修饰Block变量
@property (copy, nonatomic) void (^block) ();
当对Block第一次使用copy
的时候,Block会被拷贝到堆上;对其后续的copy
并不会拷贝多次,只会增加引用计数。
用strong
修饰Block并不会拷贝,如果Block是存储在栈上的,这个strong对象可能会成为一个野指针。
Block循环引用
常见的Block循环应用便是在Block代码区间中持有了self
。
这里需要注意的是:如果Block只是在栈上,当它执行结束之后系统会自动释放其内部变量,所以并不会发生内存泄漏。
不过我们遇到的Block经常会被传递,也就是存储在堆上,这时候就需要用weak-strong-dance来处理了。
__block
Block存储在栈上,所以Block会将它用到的所有外部变量进行拷贝,这也就是为什么在Block中修改外部变量的值不会生效(修改指针指向的内容另当别论)。
__block
做的事情就是如果这个变量在Block中被捕获,那么就将这个变量拷贝到堆上,这样一来就可以修改了。