我开始将libextobjc(https://github.com/jspahrsummers/libextobjc)集成到我的iOS应用程序中,主要是为了利用EXTScope的@strongify
和@weakify
,但在深入了解之前还有一些问题。过程
这是一个故意过于复杂的例子,试图解决如何处理这个问题:
- (void)someMethod {
if (self.someBOOL) {
_someObjectInstanceVar = [Object objectWithCompletionHandler:^{
// self reference #1
if (self.someProperty) {
// self reference #2
[[Async HTTPRequest] sendAWithID:self.property.id completionHandler:^(void (^)(NewObject *newObject) {
// self reference #3
[self showViewWithObject:newObject handler:^{
// self reference #4
[self reloadData];
}];
}];
}
}];
else {
[[Async HTTPRequest] sendBWithID:self.property.id completionHandler:^{
// self reference #5
[self reloadData];
}];
}
}
我的理解是,如果我想做异步HTTP请求,并在完成处理程序引用self内部,如[self reloadData]
,我不需要做任何强/弱的事情作为请求块本身并没有保留完成块,因此那里的保留周期没有问题。在上面的代码示例中,我认为#5 是保留周期不是问题的情况。
主要关注的是所有将块作为属性/ init参数的对象,这些对象内部保留在块属性上。在objectWithCompletionHandler
方法中,someObject
作为实例变量保存在completionHandler块中,在那里有多个对self的引用,我知道这会引起泄漏。我的主要问题是在这种情况下,您需要如何处理weakify
和strongify
以使其“更安全”?一个@weakify和@strongify调用是否足够,如下所示:
- (void)someMethod {
@weakify (self);
_someObjectInstanceVar = [Object objectWithCompletionHandler:^{
@strongify(self);
...
}
以上@strongify(self)
引用是否足以用于自引用#1,2,3和4,或者我必须(甚至可以工作)获得一个新的弱/强引用在sendAWithID
方法和嵌套reloadData
?
编辑:修复代码以使问题更有意义并修复一些语法错误。
答案 0 :(得分:76)
@strongify
如何运作调用@strongify
后,self
将在块内部具有不同的指针地址,而不是在块之外。那是因为@strongify
每次都声明一个名为self
的新局部变量。 (这就是为什么它会抑制-Wshadow
警告,它会“在局部变量影响另一个局部变量时发出警告。”)值得阅读和理解the implementation of these functions。因此,即使名称相同,也请将它们视为单独的strong
引用。
@strongify
预先假定(这不是真的)每次使用一个块都会创建一个参考周期,你可以:
- (void)someMethod {
if (self.someBOOL) {
@weakify(self);
_someObjectInstanceVar = [Object objectWithCompletionHandler:^{
@strongify(self);
// self reference #1
if (self.someProperty) {
@weakify(self);
// self reference #2
[[Async HTTPRequest] sendAWithID:self.property.id completionHandler:^(void (^)(NewObject *newObject) {
@strongify(self);
// self reference #3
@weakify(self);
[self showViewWithObject:newObject handler:^{
// self reference #4
@strongify(self);
[self reloadData];
}];
}];
}
}];
// etc…
}
但是,请记住首次使用@strongify
后的,self
将引用本地堆栈变量。当它们定义的范围结束时(只要您不将它们存储到属性或在嵌套块中使用它们),它们通常会被销毁。因此,根据您展示的代码,您只需在// self reference #1
之后使用它。
阅读unit test covering @weakify
and @strongify
将有助于澄清这些功能的正确用法。
答案 1 :(得分:8)
要回答关于块的每个嵌套级别中的多个弱化/强化实例是否有效的问题,请回答是。但是没有必要这样做,因为你的第一个@strongify定义已经为你的块的所有内部范围(以及嵌套在它中的块)定义了self。
但是,鉴于您的块具有不同的生命周期,您可能希望为每个嵌套块添加@strongify,以确保它们都将自己的保留周期保留到其内部范围。
这是解释此案例的github问题线程: https://github.com/jspahrsummers/libextobjc/issues/45
答案 2 :(得分:1)
在由“self”保持的块内调用“self”将导致“Retain Cycles”并因此导致内存泄漏。所以理想情况就是这样:
@interface A: NSObject // Some interface A
@property (nonatomic, copy, readwrite) dispatch_block_t someBlock; // See block is strongly retained here.
@end
*******************************************************
@implementation A
- (void) someAPI
{
__weak A * weakSelf = self; // Assign self to weakSelf and use it
// enter code here inside block to break retain cycles.
self.someBlock =
^{
A * strongSelf = weakSelf; // Assign weak self to strongSelf before
// using it. This is because weakSelf can go nil anytime and it may happen
// that only few lines from block get executed before weakSelf goes nil,
// and hence code may be in some bad state.
if (strongSelf != nil)
{
// Use strongSelf.
[strongSelf doSomethingAwesome];
[strongSelf doSomethingAwesomeAgain];
}
};
}
@end
如果块没有被“self”保留,那么在块内部使用“self”是安全的,它们不会创建保留周期。
注意:内存管理概念与使用“libextobjc”库保持一致。