iOS的block内存管理
初始情况下:
block本身、__block修饰的变量以及在block内部使用的变量都是在栈里的。
__block修饰的变量的地址会作为实参传入block块内部(暂时先这么理解,实际比较复杂)。block使用的外部变量被const拷贝到了block内部。也就是block使用的外部变量和这个外部变量本身没有关系。
copy方法之后
被拷贝的方法还是在栈上。但是拷贝之后的block已经被放在了heap(堆)上。同时__block修饰的变量被移动到了堆上,原来在栈上的已经不存在。还有block的外部变量的const拷贝也被拷贝到了堆上。
如果block块使用的外部变量是一个refernce的对象,那么这个对象的引用计数会增加1。
堆上的block再做一次copy之后,只是引用计数增加1,但是不会重新再做拷贝动作。
retain操作
由于retain是有返回值的。retain要求返回的地址和调用对象的地址一致。但是block的地址可能是会变的(尤其是从栈到堆的过程),所以对block做retain操作是没有用处的。什么都不会做!
销毁(或内存回收时)
heap上的block块先于stack上的被销毁时,如调用release销毁堆上的block块。heap中的block块在引用计数变为0的时候被销毁。而__block修饰的变量还在heap中,因为stack还要使用,同时栈上的block快也要使用。
当heap上的block块晚于stack时,stack会被清除。在heap中的block块在调用release减少引用计数到0的时候释放内存。
block和Objective-C对象
如果对block做拷贝操作,block会对其内部使用的对象生成强引用。
如果在block块内部使用了:
- 以引用的方式使用了类的成员变量,那么这个block会对self产生强引用。
- 以值得方式使用了类的成员变量,那么这个block会对这个变量本身产生强引用。
例如:
dispatch_async(queue, ^{ // instanceVariable is used by reference, a strong reference is made to self doSomethingWithObject(instanceVariable); }); id localVariable = instanceVariable; dispatch_async(queue, ^{ /* localVariable is used by value, a strong reference is made to localVariable (and not to self). */ doSomethingWithObject(localVariable); });
以上可以通过__block修饰符来改变。是的__block是另外的一种存储类型,就像copy、retain什么的。这样这个引用就直接被传递到了block中。
但是__block是不可以用来消除对self的循环引用的。