iOS 正确使用@weakify(self) 和@strongify(self)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28305356/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
iOS Proper Use of @weakify(self) and @strongify(self)
提问by Mike
I'm starting to integrate libextobjc (https://github.com/jspahrsummers/libextobjc) into my iOS application primarily to take advantage of EXTScope's @strongify
and @weakify
, but have a few questions before proceeding too deep into the process.
我开始将 libextobjc ( https://github.com/jspahrsummers/libextobjc)集成到我的 iOS 应用程序中,主要是为了利用 EXTScope 的@strongify
和@weakify
,但在深入研究该过程之前还有一些问题。
Here's an example that's intentionally overly-complicated to try to suss out how to handle this:
这是一个故意过于复杂的例子,试图弄清楚如何处理这个问题:
- (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];
}];
}
}
My understanding is that if I want to do anything like an async HTTP request, and inside the completion handler reference self, like [self reloadData]
, I don't need to do anything with strong/weak as the request block itself isn't retaining the completion block, so there's no problems with retain cycles there. In the above code example, I think #5is a case where a retain cycles isn't an issue.
我的理解是,如果我想做任何类似异步 HTTP 请求的事情,并且在完成处理程序中引用 self 之类的[self reloadData]
,我不需要对强/弱做任何事情,因为请求块本身不保留完成块,所以那里的保留周期没有问题。在上面的代码示例中,我认为#5是保留周期不是问题的情况。
The main concern are all of the objects that take a block as a property/init param, that are internally holding onto the block properties. Inside the objectWithCompletionHandler
method, where someObject
holds onto the completionHandler block as an instance variable, there are multiple references to self there that I do know would cause a leak. My main question is in such a case, how would you need to handle weakify
and strongify
to make it "safer"? Would one @weakify and @strongify call each be sufficient, like the following:
主要关注的是所有将块作为属性/初始化参数的对象,它们在内部保持块属性。在objectWithCompletionHandler
方法内部,someObject
将completionHandler 块作为实例变量保存在该方法中,有多个对self 的引用,我知道这会导致泄漏。我的主要问题是在这种情况下,您需要如何处理weakify
并strongify
使其“更安全”?一个@weakify 和@strongify 调用是否就足够了,如下所示:
- (void)someMethod {
@weakify (self);
_someObjectInstanceVar = [Object objectWithCompletionHandler:^{
@strongify(self);
...
}
Would the above @strongify(self)
reference be sufficient to use for self references #1, 2, 3, and 4, or do I have to (and would it even work) get a new weak/strong reference to use inside the sendAWithID
method and the nested reloadData
?
上述@strongify(self)
引用是否足以用于自引用 #1、2、3 和 4,或者我是否必须(甚至可以工作)获取新的弱/强引用以在sendAWithID
方法和嵌套中使用reloadData
?
EDIT: Fixed code to have question make more sense and fix some syntax errors.
编辑:固定代码有问题更有意义并修复一些语法错误。
回答by Aaron Brager
How @strongify
works
如何@strongify
工作
After @strongify
is called, self
will have a different pointer address insidethe block than it will outsidethe block. That's because @strongify
declares a new local variable called self
each time. (This is why it suppresses the -Wshadow
warning, which will “warn whenever a local variable shadows another local variable.”) It's worth reading and understanding the implementation of these functions. So even though the names are the same, treat them as separate strong
references.
之后@strongify
被调用时,self
将具有不同的指针地址内的块比它会以外的块。那是因为每次都会@strongify
声明一个新的局部变量self
。(这就是它抑制-Wshadow
警告的原因,它会“在一个局部变量隐藏另一个局部变量时发出警告”。)这些函数的实现值得阅读和理解。因此,即使名称相同,请将它们视为单独的strong
引用。
Using @strongify
in your code
使用@strongify
在你的代码
Presupposing (which is not true) that each use of a block would create a reference cycle, you could:
假设(这是不正确的)块的每次使用都会创建一个引用循环,您可以:
- (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…
}
However, remember that after your first use of @strongify
, self
will refer to local, stack variables. These will typically get destroyed when the scope in which they're defined ends (as long as you aren't storing them to properties or using them in a nested block). So based on the code you showed, you only need it after // self reference #1
.
但是,请记住,在您第一次使用之后@strongify
,self
将引用本地、堆栈变量。这些通常会在定义它们的范围结束时被销毁(只要您没有将它们存储到属性或在嵌套块中使用它们)。因此,根据您显示的代码,您只需要在// self reference #1
.
See Also
也可以看看
Reading the unit test covering @weakify
and @strongify
will help clarify the correct usage of these functions.
读单元测试覆盖@weakify
,并@strongify
有助于澄清这些功能的正确用法。
回答by carlossless
To answer your question of whether multiple instances of weakify/strongify in each nested level of your blocks works, then yes. But there's no need to do that because, your first @strongify definition already defines self for all of the inner scope of your block (and the blocks that are nested in it).
要回答您的问题,即块的每个嵌套级别中的多个弱化/强化实例是否有效,然后是。但是没有必要这样做,因为您的第一个 @strongify 定义已经为块的所有内部范围(以及嵌套在其中的块)定义了 self 。
However, given that your blocks have different lifetimes, you might want to add @strongify for each nested block to make sure they all hold their own retain cycle to their inner scope.
但是,鉴于您的块具有不同的生命周期,您可能希望为每个嵌套块添加 @strongify,以确保它们都在其内部范围内保持自己的保留周期。
Here's the github issue thread that explains this case: https://github.com/jspahrsummers/libextobjc/issues/45
这是解释这种情况的 github 问题线程:https: //github.com/jspahrsummers/libextobjc/issues/45
回答by gagarwal
Calling "self" inside the block that in hold by "self" will lead to "Retain Cycles" and hence memory leaks. So ideally it goes like:
在“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
If the block is not retained by "self", then its safe to just use "self" inside blocks and they won't create retain-cycles.
如果块没有被“self”保留,那么在块内只使用“self”是安全的,它们不会创建保留循环。
Note: Memory management concept remains same with use of "libextobjc" library.
注意:内存管理概念与“libextobjc”库的使用保持一致。