我对 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];
}
进程操作.h
@interface ProcessOperation : NSOperation
{
NSMutableArray *dataArr;
NSMutableArray *rowHeightsArr;
void (^callback)(NSMutableArray *rows);
}
流程操作.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);
}
}
最好不要将注意力集中在讨论的 strong
或 weak
部分。而是专注于 cycle 部分。
保留循环是当对象 A 保留对象 B,而对象 B 保留对象 A 时发生的循环。在这种情况下,如果任一对象被释放:
对象 A 不会被释放,因为对象 B 持有对它的引用。
但是只要对象 A 有对它的引用,对象 B 就永远不会被释放。
但是对象 A 永远不会被释放,因为对象 B 持有对它的引用。
无止境
因此,这两个对象将在程序的整个生命周期中一直在内存中徘徊,即使如果一切正常,它们应该被释放。
所以,我们担心的是保留周期,而创建这些周期的块本身并没有什么。这不是问题,例如:
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
[self doSomethingWithObject:obj];
}];
该块保留 self
,但 self
不保留该块。如果释放其中一个或另一个,则不会创建任何循环,并且所有内容都会按应有的方式释放。
你遇到麻烦的地方是这样的:
//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
根据定义已经有一个对块的 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];
}];
我完全同意@jemmons:
但这不应该是您在处理调用 self 的块时遵循的默认模式!这应该只用于打破自我和块之间的保留循环。如果你要在任何地方都采用这种模式,你就会冒着将块传递给在 self 被释放后执行的东西的风险。 //可疑示例:__weak MyObject *weakSelf = self; [[SomeOtherObject alloc] initWithCompletion:^{ //当它被调用时,“weakSelf”可能为 nil,因为它没有被保留! [weakSelf doSomething]; }];
为了克服这个问题,可以在块内的 weakSelf
上定义一个强引用:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
MyObject *strongSelf = weakSelf;
[strongSelf doSomething];
}];
->
),strongSelf 有帮助的地方,您希望保证您确实获得了有效的引用并在整个操作集中持续保持它,例如 if ( strongSelf ) { /* several operations */ }
您不必总是使用弱引用。如果你的块没有被保留,而是被执行然后被丢弃,你可以强捕获自我,因为它不会创建一个保留循环。在某些情况下,您甚至希望块保持自身直到块完成,这样它就不会过早地释放。但是,如果您强烈捕获块,并且在捕获自身内部,它将创建一个保留循环。
正如 Leo 指出的那样,您添加到问题中的代码不会暗示一个强大的参考周期(也就是保留周期)。一个可能导致强引用周期的与操作相关的问题是,如果操作没有被释放。虽然您的代码片段表明您尚未将操作定义为并发,但如果您定义了操作,那么如果您从未发布 isFinished
,或者如果您有循环依赖项或类似的东西,它将不会被释放。如果操作没有被释放,视图控制器也不会被释放。我建议在您的操作的 dealloc
方法中添加一个断点或 NSLog
并确认它被调用。
你说:
我理解保留周期的概念,但我不太确定块中会发生什么,所以这让我有点困惑
块发生的保留周期(强引用周期)问题就像您熟悉的保留周期问题一样。块将保持对块内出现的任何对象的强引用,并且在块本身被释放之前它不会释放这些强引用。因此,如果块引用 self
,甚至只是引用 self
的实例变量,这将保持对自身的强引用,直到块被释放(或者在这种情况下,直到 NSOperation
子类被释放。
有关详细信息,请参阅使用 Objective-C 编程:使用块文档的 Avoid Strong Reference Cycles when Capturing self 部分。
如果您的视图控制器仍未释放,您只需确定未解析的强引用所在的位置(假设您确认 NSOperation
正在被释放)。一个常见的例子是使用重复的 NSTimer
。或者某些自定义 delegate
或其他错误地维护 strong
引用的对象。您通常可以使用 Instruments 来追踪对象从何处获得强引用,例如:
https://i.stack.imgur.com/y0eLE.png
或者在 Xcode 5 中:
https://i.stack.imgur.com/kn1sF.png
一些解释忽略了有关保留循环的条件[如果一组对象通过一个强关系圈连接,即使没有来自组外的强引用,它们也会相互保持活动状态。]有关更多信息,请阅读 {1 }
这是您可以在块内使用 self 的方式:
//调用块
NSString *returnedText= checkIfOutsideMethodIsCalled(self);
NSString* (^checkIfOutsideMethodIsCalled)(*)=^NSString*(id obj)
{
[obj MethodNameYouWantToCall]; // this is how it will call the object
return @"Called";
};
不定期副业成功案例分享
-setCompleteionBlockWithSuccess:failure:
方法的实现。但是如果paginator
归ViewController
所有,并且在ViewController
被释放后这些块不会被调用,那么使用__weak
引用将是安全的举措(因为self
拥有拥有块,因此当块调用它时可能仍然存在,即使它们不保留它)。但这是很多“如果”。这真的取决于这应该做什么。MyObject
和SomeOtherObject
都拥有该块。但是因为块对MyObject
的引用是weak
,所以块不拥有MyObject
。因此,虽然只要存在 eitherMyObject
orSomeOtherObject
就保证该块存在,但不能保证只要该块存在MyObject
就会存在.MyObject
可以完全解除分配,只要SomeOtherObject
仍然存在,块就仍然存在。