在ARC中总是把自己的弱引用变成块?

我对Objective-C中的块使用有点困惑。 我目前使用ARC,在我的应用程序中有相当多的块,目前总是指self而不是它的弱引用。 这可能是这些块保持self ,防止交易的原因吗? 问题是,我应该总是在一个块中使用一个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

 @interface ProcessOperation : NSOperation { NSMutableArray *dataArr; NSMutableArray *rowHeightsArr; void (^callback)(NSMutableArray *rows); } 

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); } } 

这有助于不把注意力集中在讨论的weak部分。 而是专注于周期部分。

保持周期是当对象A保持对象B 并且对象B保持对象A时发生的循环。在这种情况下,如果任一对象被释放:

  • 对象A不会被释放,因为对象B持有对它的引用。
  • 但是只要对象A有引用,对象B就不会被释放。
  • 但是对象A永远不会被释放,因为对象B持有对它的引用。
  • 无限的

因此,这两个对象只会在程序的整个生命周期中回忆起来,尽pipe如果一切正常,它们应该被释放。

所以,我们担心的是保留周期 ,并且没有任何关于自己创build这些周期的块。 这不是一个问题,例如:

 [myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){ [self doSomethingWithObject:obj]; }]; 

块保留self ,但self不保留块。 如果一个或另一个被释放,则不会创build循环,并且所有内容都应该被释放。

你遇到麻烦的地方是这样的:

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

现在,你的对象( self )有一个明确的strong参考块。 这个块对self有一个隐含的强烈的self 。 这是一个循环,现在这两个对象都不会被正确释放。

因为,在这样的情况下, self 定义已经有了一个strong的块的引用,通常最简单的解决方法是使用一个明确的弱引用self来使用块:

 __weak MyObject *weakSelf = self; [self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) { [weakSelf doSomethingWithObj:obj]; }]; 

但是,这不应该是你在处理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]; }]; 

你不必总是使用一个弱引用。 如果您的块没有保留,但执行,然后丢弃,您可以强烈地捕获自己,因为它不会创build一个保留周期。 在某些情况下,你甚至希望块保持自己,直到块完成,所以它不提前释放。 但是,如果您强烈地捕捉该块,并在捕捉自己内部,则会创build一个保留循环。

我完全同意@jemmons。

“但是,这不应该是你在处理自称的块时所遵循的默认模式!这只能用来打破本来和块之间的保留循环,如果你到处采用这种模式, 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]; }]; 

为了解决这个问题,我们可以定义一个关于块内部弱自身的强有力的参考。

 __weak MyObject *weakSelf = self; [[SomeOtherObject alloc] initWithCompletion:^{ MyObject *strongSelf = weakSelf; [strongSelf doSomething]; }]; 

正如Leo所指出的那样,您添加到您的问题中的代码并不意味着强烈的参考周期(也就是保留周期)。 一个操作相关的问题可能会导致强大的参考周期,如果操作没有得到释放。 虽然你的代码片断表明你没有定义你的操作是并发的,但如果你有,它不会被释放,如果你从来没有发布isFinished ,或者如果你有循环依赖,或类似的东西。 如果操作没有被释放,视图控制器也不会被释放。 我会build议在你的操作的dealloc方法中添加一个断点或NSLog ,并确认它正在调用。

你说:

我理解保留周期的概念,但我不太清楚块中会发生什么,所以会让我困惑一些

块中出现的保留周期(强参考周期)问题就像您熟悉的保留周期问题。 一个块将保持对块内出现的任何对象的强引用,并且不会释放这些强引用,直到块本身被释放。 因此,如果块引用是self ,或者甚至只是引用一个self的实例variables,那么它将保持对自我的强引用,直到块被释放(或者在这种情况下,直到NSOperation子类被释放)才会被NSOperation

有关详细信息,请参阅使用Objective-C:使用块文档捕获自我部分时避免强参考循环 。

如果你的视图控制器还没有被释放,你只需要确定未parsing的强引用所在的位置(假设你确认了NSOperation被取消分配)。 一个常见的例子是使用重复的NSTimer 。 或者一些自定义delegate或其他对象错误地保持strong参考。 您可以经常使用“工具”来查找对象获取其强引用的位置,例如:

在Xcode 6中记录引用计数

或者在Xcode 5中:

在Xcode 5中记录引用计数

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

这是你如何使用块内的自我:

/ /调用块

  NSString *returnedText= checkIfOutsideMethodIsCalled(self); 

 NSString* (^checkIfOutsideMethodIsCalled)(*)=^NSString*(id obj) { [obj MethodNameYouWantToCall]; // this is how it will call the object return @"Called"; }; 

用一个例子来解释可能是最好的。 看下面。

 [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){ [self doSomethingWithObject:obj]; }]; 

如果“自我”是一个视图控制器,并被父视图控制器解散,那么我希望该视图控制器从内存释放。 但是,上面的方法会对自己有很强的参考,因此视图控制器会保留在内存中,直到enumerateObjects方法完全执行完毕。

如果我们通过一个对自己的弱言谈,当视图控制器被解散时,它将成为零。

 __weak CustomViewController *weakSelf = self; [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){ [weakSelf doSomethingWithObject:obj]; }];