简体   繁体   English

在ARC中总是将自我的弱引用传递给块?

[英]Always pass weak reference of self into block in ARC?

I am a little confused about block usage in Objective-C. 我对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. 我目前使用ARC,我的应用程序中有很多块,目前总是指self而不是弱引用。 May that be the cause of these blocks retaining self and keeping it from being dealloced ? 这可能是导致这些障碍保留self并防止其被解除分配的原因吗? The question is, should I always use a weak reference of self in a block ? 问题是,我是否应该总是在一个区块中使用selfweak引用?

-(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 ProcessOperation.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);
    }
}

It helps not to focus on the strong or weak part of the discussion. 这不仅有助于把重点放在strongweak的讨论的一部分。 Instead focus on the cycle part. 而是专注于循环部分。

A retain cycle is a loop that happens when Object A retains Object B, and Object 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. 对象A不会被释放,因为对象B保存对它的引用。
  • But Object B won't ever be deallocated as long as Object A has a reference to it. 但是只要对象A具有对它的引用,对象B就不会被释放。
  • But Object A will never be deallocated because Object B holds a reference to it. 但是对象A永远不会被释放,因为对象B拥有对它的引用。
  • ad infinitum 无限的

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. 该块保留self ,但self不保留该块。 If one or the other is released, no cycle is created and everything gets deallocated as it should. 如果释放了一个或另一个,则不会创建任何循环,并且所有内容都会被取消分配。

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. 现在,您的对象( self )具有对块的明确strong引用。 And the block has an implicit strong reference to self . 该块具有对self隐含强烈引用。 That's a cycle, and now neither object will be deallocated properly. 这是一个循环,现在这两个对象都不会被正确释放。

Because, in a situation like this, self by definition already 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 follow when dealing with blocks that call self ! 但这不应该是你在处理调用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之后执行的东西。

//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];
}];

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. 但是,如果您强烈捕获块,并且在捕获自身内部,则会创建一个保留周期。

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." “但这不应该是你在处理调用self的块时所遵循的默认模式!这应该仅用于打破自我和块之间的保留周期。如果你在各处采用这种模式,你就是' d冒着将一个块传递给自我被解除分配后执行的东西的风险。“

//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 over come 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];
}];

As Leo points out, the code you added to your question would not suggest a strong reference cycle (aka, retain cycle). 正如Leo指出的那样,您添加到问题中的代码不会建议强大的参考周期(也就是保留周期)。 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. 虽然你的代码片段表明你没有将你的操作定义为并发,但是如果你有,那么如果你从未发布过isFinished ,或者如果你有循环依赖关系,或者类似的东西,它就不会被释放。 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. 我建议在你的操作的dealloc方法中添加一个断点或NSLog ,并确认它已被调用。

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 self section of the Programming with Objective-C: Working with Blocks document. 有关更多信息,请参阅“ 使用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). 如果您的视图控制器仍未发布,您只需确定未解析的强引用所在的位置(假设您确认NSOperation已取消分配)。 A common example is the use of a repeating NSTimer . 一个常见的例子是使用重复的NSTimer Or some custom delegate or other object that is erroneously maintaining a strong reference. 或者某些自定义delegate或其他错误维护strong引用的对象。 You can often use Instruments to track down where objects are getting their strong references, eg: 您通常可以使用Instruments来追踪对象获得强引用的位置,例如:

在Xcode 6中记录引用计数

Or in Xcode 5: 或者在Xcode 5中:

在Xcode 5中记录引用计数

一些解释忽略了关于保留周期的条件[如果一组对象通过一系列强关系连接,即使没有来自组外的强引用,它们也会保持活着。]有关更多信息,请阅读文档

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";


};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM