ios 总是将 self 的弱引用传递到 ARC 中的块中?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20030873/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Always pass weak reference of self into block in ARC?
提问by the_critic
I am a little confused about block usage in Objective-C. I currently use ARC and I have quite a lot of blocks in my app, currently always referring to self
instead of its weak reference. May that be the cause of these blocks retaining self
and keeping it from being dealloced ? The question is, should I always use a weak
reference of self
in a block ?
我对 Objective-C 中的块使用有点困惑。我目前使用 ARC 并且我的应用程序中有很多块,目前总是引用self
而不是它的弱引用。这可能是这些块保留self
并阻止它被释放的原因吗?问题是,我应该总是在块中使用weak
of的引用self
吗?
-(void)handleNewerData:(NSArray *)arr
{
ProcessOperation *operation =
[[ProcessOperation alloc] initWithDataToProcess:arr
completion:^(NSMutableArray *rows) {
dispatch_async(dispatch_get_main_queue(), ^{
[self updateFeed:arr rows:rows];
});
}];
[dataProcessQueue addOperation:operation];
}
ProcessOperation.h
流程操作.h
@interface ProcessOperation : NSOperation
{
NSMutableArray *dataArr;
NSMutableArray *rowHeightsArr;
void (^callback)(NSMutableArray *rows);
}
ProcessOperation.m
ProcessOperation.m
-(id)initWithDataToProcess:(NSArray *)data completion:(void (^)(NSMutableArray *rows))cb{
if(self =[super init]){
dataArr = [NSMutableArray arrayWithArray:data];
rowHeightsArr = [NSMutableArray new];
callback = cb;
}
return self;
}
- (void)main {
@autoreleasepool {
...
callback(rowHeightsArr);
}
}
回答by jemmons
It helps not to focus on the strong
or weak
part of the discussion. Instead focus on the cyclepart.
不要把注意力集中在讨论的一部分strong
或weak
一部分上是有帮助的。而是专注于循环部分。
A retain cycleis a loop that happens when Object A retains Object B, andObject B retains Object A. In that situation, if either object is released:
甲保留循环是一个循环时对象A保留对象B,发生这种情况和对象B保留对象A在这种情况下,如果任一对象被释放:
- Object A won't be deallocated because Object B holds a reference to it.
- But Object B won't ever be deallocated as long as Object A has a reference to it.
- But Object A will never be deallocated because Object B holds a reference to it.
- ad infinitum
- 对象 A 不会被释放,因为对象 B 持有对它的引用。
- 但是只要对象 A 有对它的引用,对象 B 就永远不会被释放。
- 但是对象 A 永远不会被释放,因为对象 B 持有对它的引用。
- 无止境
Thus, those two objects will just hang around in memory for the life of the program even though they should, if everything were working properly, be deallocated.
因此,这两个对象将在程序的整个生命周期中停留在内存中,即使它们应该(如果一切正常)被释放。
So, what we're worried about is retain cycles, and there's nothing about blocks in and of themselves that create these cycles. This isn't a problem, for example:
所以,我们所担心的是保留周期,而块本身并没有创造这些周期。这不是问题,例如:
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
[self doSomethingWithObject:obj];
}];
The block retains self
, but self
doesn't retain the block. If one or the other is released, no cycle is created and everything gets deallocated as it should.
块保留self
,但self
不保留块。如果释放一个或另一个,则不会创建循环,并且所有内容都将按应有的方式解除分配。
Where you get into trouble is something like:
你遇到麻烦的地方是这样的:
//In the interface:
@property (strong) void(^myBlock)(id obj, NSUInteger idx, BOOL *stop);
//In the implementation:
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[self doSomethingWithObj:obj];
}];
Now, your object (self
) has an explicit strong
reference to the block. And the block has an implicitstrong reference to self
. That's a cycle, and now neither object will be deallocated properly.
现在,您的对象 ( self
) 具有strong
对该块的显式引用。并且该块具有对 的隐式强引用self
。这是一个循环,现在两个对象都不会被正确释放。
Because, in a situation like this, self
by definitionalready has a strong
reference to the block, it's usually easiest to resolve by making an explicitly weak reference to self
for the block to use:
因为,在这种情况下,self
根据定义,已经有一个strong
对块的引用,通常最容易解决的办法是self
为要使用的块创建一个显式弱引用:
__weak MyObject *weakSelf = self;
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[weakSelf doSomethingWithObj:obj];
}];
But this should not be the default pattern you followwhen dealing with blocks that call self
! This should only be used to break what would otherwise be a retain cycle between self and the block. If you were to adopt this pattern everywhere, you'd run the risk of passing a block to something that got executed after self
was deallocated.
但这不应该是您在处理调用self
! 这应该只用于打破 self 和块之间的保留循环。如果你在任何地方都采用这种模式,你会冒着将块传递给在self
释放后执行的东西的风险。
//SUSPICIOUS EXAMPLE:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
//By the time this gets called, "weakSelf" might be nil because it's not retained!
[weakSelf doSomething];
}];
回答by Leo Natan
You don't have to always use a weak reference. If your block is not retained, but executed and then discarded, you can capture self strongly, as it will not create a retain cycle. In some cases, you even want the block to hold the self until the completion of the block so it does not deallocate prematurely. If, however, you capture the block strongly, and inside capture self, it will create a retain cycle.
您不必总是使用弱引用。如果你的块没有被保留,而是被执行然后被丢弃,你可以强烈地捕获自我,因为它不会创建一个保留循环。在某些情况下,您甚至希望块在块完成之前一直保持 self ,这样它就不会过早地解除分配。但是,如果您强烈地捕获块,并在捕获自身内部,它将创建一个保留循环。
回答by Ilker Baltaci
I totally agree with @jemmons:
我完全同意@jemmons:
But this should not be the default pattern you follow when dealing with blocks that call self! This should only be used to break what would otherwise be a retain cycle between self and the block. If you were to adopt this pattern everywhere, you'd run the risk of passing a block to something that got executed after self was deallocated.
//SUSPICIOUS EXAMPLE: __weak MyObject *weakSelf = self; [[SomeOtherObject alloc] initWithCompletion:^{ //By the time this gets called, "weakSelf" might be nil because it's not retained! [weakSelf doSomething]; }];
但这不应该是您在处理调用 self! 的块时遵循的默认模式!这应该只用于打破 self 和块之间的保留循环。如果你在任何地方都采用这种模式,你会冒着将一个块传递给在 self 被释放后执行的东西的风险。
//SUSPICIOUS EXAMPLE: __weak MyObject *weakSelf = self; [[SomeOtherObject alloc] initWithCompletion:^{ //By the time this gets called, "weakSelf" might be nil because it's not retained! [weakSelf doSomething]; }];
To overcome this problem one can define a strong reference over the weakSelf
inside the block:
为了克服这个问题,可以weakSelf
在块内部定义一个强引用:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
MyObject *strongSelf = weakSelf;
[strongSelf doSomething];
}];
回答by Rob
As Leo points out, the code you added to your question would not suggest a strong reference cycle (a.k.a., retain cycle). One operation-related issue that could cause a strong reference cycle would be if the operation is not getting released. While your code snippet suggests that you have not defined your operation to be concurrent, but if you have, it wouldn't be released if you never posted isFinished
, or if you had circular dependencies, or something like that. And if the operation isn't released, the view controller wouldn't be released either. I would suggest adding a breakpoint or NSLog
in your operation's dealloc
method and confirm that's getting called.
正如 Leo 指出的那样,您添加到问题中的代码不会建议强引用循环(又名保留循环)。一个可能导致强引用循环的与操作相关的问题是,如果操作没有被释放。虽然您的代码片段表明您尚未将操作定义为并发,但如果您定义了,如果您从未发布过isFinished
,或者您有循环依赖,或类似的东西,它就不会被释放。如果操作没有被释放,视图控制器也不会被释放。我建议NSLog
在您的操作dealloc
方法中添加一个断点,并确认它被调用了。
You said:
你说:
I understand the notion of retain cycles, but I am not quite sure what happens in blocks, so that confuses me a little bit
我理解保留周期的概念,但我不太确定块中会发生什么,所以这让我有点困惑
The retain cycle (strong reference cycle) issues that occur with blocks are just like the retain cycle issues you're familiar with. A block will maintain strong references to any objects that appear within the block, and it will not release those strong references until the block itself is released. Thus, if block references self
, or even just references an instance variable of self
, that will maintain strong reference to self, that is not resolved until the block is released (or in this case, until the NSOperation
subclass is released.
块发生的保留循环(强引用循环)问题就像您熟悉的保留循环问题。块将维护对出现在块中的任何对象的强引用,并且在块本身被释放之前不会释放这些强引用。因此,如果块引用self
,或者甚至只是引用 的实例变量self
,这将保持对 self 的强引用,直到块被释放(或者在这种情况下,直到NSOperation
子类被释放。
For more information, see the Avoid Strong Reference Cycles when Capturing selfsection of the Programming with Objective-C: Working with Blocksdocument.
有关更多信息,请参阅使用 Objective-C 编程:使用块文档的捕获自身时避免强引用循环部分。
If your view controller is still not getting released, you simply have to identify where the unresolved strong reference resides (assuming you confirmed the NSOperation
is getting deallocated). A common example is the use of a repeating NSTimer
. Or some custom delegate
or other object that is erroneously maintaining a strong
reference. You can often use Instruments to track down where objects are getting their strong references, e.g.:
如果您的视图控制器仍未被释放,您只需确定未解析的强引用所在的位置(假设您确认NSOperation
正在释放)。一个常见的例子是使用重复NSTimer
. 或者delegate
错误地维护strong
引用的某些自定义或其他对象。你可以经常使用 Instruments 来追踪对象在哪里获得它们的强引用,例如:
Or in Xcode 5:
或者在 Xcode 5 中:
回答by Danyun Liu
Some explanation ignore a condition about the retain cycle [If a group of objects is connected by a circle of strong relationships, they keep each other alive even if there are no strong references from outside the group.] For more information, read the document
一些解释忽略了关于保留循环的条件[如果一组对象由一个强关系圈连接,即使没有来自组外的强引用,它们也会保持彼此的活力。] 有关更多信息,请阅读文档
回答by Ranjeet Singh
This is how you can use the self inside the block:
这是您可以在块内使用 self 的方法:
//calling of the block
//调用块
NSString *returnedText= checkIfOutsideMethodIsCalled(self);
NSString* (^checkIfOutsideMethodIsCalled)(*)=^NSString*(id obj)
{
[obj MethodNameYouWantToCall]; // this is how it will call the object
return @"Called";
};