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