Manage objective c object memory in Block

来源:互联网 发布:主机公园 php搭建 编辑:程序博客网 时间:2024/05/17 23:25

Look at the document from Apple:

When a block is copied, it creates strong references to object variables used within the block. If you use a block
within the implementation of a method:
●If you access an instance variable by reference, a strong reference is made to self;
●If you access an instance variable by value, a strong reference is made to the variable.
The following examples illustrate the two different situations:

dispatch_async(queue, ^{// instanceVariable is used by reference, a strong reference is made to selfdoSomethingWithObject(instanceVariable);});id localVariable = instanceVariable;dispatch_async(queue, ^{/*localVariable is used by value, a strong reference is made to localVariable(and not to self).Blocks and VariablesObject and Block Variables*/doSomethingWithObject(localVariable);});
To override this behavior for a particular object variable, you can mark it with the __block storage type
modifier.

使用方法总结如下:(只对应NON-ARC的情况)

  • 如果block没有被copy,则block中的变量在stack空间中被创建。一旦此scope执行完毕,此stack空间被释放。
  • 如果block被copy,则block中的所有变量被拷贝到heap中。
    此时,可以看作所有的变量都被重新拷贝了一份。
    1. 对于 int, float等基本类型的变量,不需要担心release的问题,但是赋值操作将不会影响到stack空间中的变量(即block scope外的原变量)。
    2. 对于objective c对象,如果直接调用self中变量,则self会被retain。由于在self中copy了这个block,会造成循环引用。
    解决方法是:声明__block blockSelf = self, 在block中调用blockSelf的变量。
    (注意:TestClass *blockSelf = self, blockSelf和self都指向同一个内存地址,当self先于blockSelf被释放时,blockSelf就变成了野指针!!
    真的是这样吗?实际测试过,如果加上了__block,self不会先释放,会等到block执行完之后才释放。)
  • 如果是
id localVariable = instanceVariable;
把localVariable传进block,则localVariable会被retain。在block在最后需要release在执行完此block后,localVariable会被自动release(前提是此block要被释放)
例子:

@ property (nonatomic, copy) RequestCompleteBlock copiedCompleteBlock;

@ property (nonatomic, copy) RequestFailureBlock failure;

TestClass *testObject = [TestClass node]; self.copiedCompleteBlock = ^(NSData *data) { [self performSelector:@selector(execute:data:) withObject:testObject afterDelay:3.0f]; }; self.failure = ^(NSError *error){ testObject.name = @"wang"; }; // set request type, HTML FWURLRequest *defaultRequest = [FWURLRequest defaultRequest]; defaultRequest.requestType = FWE_REQUESTTYPE_HTML; [defaultRequest requestWithURL:url paramsDicData:params completeBlock:self.copiedCompleteBlock failBlock:self.failure];
因为在copiedCompleteBlock和failure中testObject都被retain了,但是这两个block只有一个会被调用到,所以无论如何testObject都不会被释放。

3. 如果声明了__block的objective c对象,可以理解成,此对象的指针地址被拷贝到了heap(其指针实际指向的地址并没有被拷贝),它指向实际地址空间的retain count并没有增加,所以读写都有效。也不需要进行额外的release。
注意,如上例子,如果
__block TestClass *testObject = [TestClass node];
因为testObject不会被retain,当程序运行到当前scope外时,此testObject会被release,如果在此之后copiedCompleteBlock才被调用,则程序会崩溃。
建议:
1. 对于非self的object,不要使用__block,因为在block释放时会自动释放这些object。
2. 对于self的object,
You have a few options.1) When not using ARC, the __block qualifier on an object pointer causes it to be non-retained. Thus:__block Whatever *weakSelf = self;^{ [weakSelf ...]; }2) When using ARC on 10.7/iOS5, the __weak qualifier gets the same job done, but even better. __weak qualified variables are zeroing, so they're safer to use.3) If you're using ARC but have to support 10.6/iOS4, the __unsafe_unretained qualifier will give you a non-zeroing weak pointer.4) My MAZeroingWeakRef library provides functionality similar to __weak, but works without ARC and on older OSes. For example:MAZeroingWeakRef *weakSelfRef = [MAZeroingWeakRef refWithTarget: self];^{ [[weakSelfRef target] ...]; }Since this is a zeroing weak reference, it's safer to use than __block or __unsafe_unretained. You can get it here with a liberal license:https://github.com/mikeash/MAZeroingWeakRef

Ref:
http://lcwangchao.github.com/IOS/2012/07/16/block_retain_cycle/

原创粉丝点击