iOS正确使用@weakify(self)和@strongify(self)

时间:2022-01-19 08:12:49

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 #5 is 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:

主要关注的是所有将块作为属性/ init参数的对象,这些对象内部保留在块属性上。在objectWithCompletionHandler方法中,someObject作为实例变量保存在completionHandler块中,有多个对self的引用,我知道这会引起泄漏。我的主要问题是在这种情况下,你需要如何处理弱化和强化以使其“更安全”?一个@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(自我)引用是否足以用于自引用#1,2,3和4,或者我必须(甚至可以工作)获得一个新的弱/强引用以在sendAWithID中使用方法和嵌套的reloadData?

EDIT: Fixed code to have question make more sense and fix some syntax errors.

编辑:修复代码有问题更有意义,并修复一些语法错误。

3 个解决方案

#1


66  

How @strongify works

After @strongify is called, self will have a different pointer address inside the block than it will outside the 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警告,它会“在局部变量影响另一个局部变量时发出警告。”)值得阅读并理解这些函数的实现。因此即使名称相同,也将它们视为单独的强引用。

Using @strongify in your code

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将引用本地堆栈变量。当它们定义的范围结束时(只要您不将它们存储到属性或在嵌套块中使用它们),它们通常会被销毁。因此,根据您展示的代码,您只需在//自引用#1之后使用它。

See Also

Reading the unit test covering @weakify and @strongify will help clarify the correct usage of these functions.

阅读涵盖@ weakify和@strongify的单元测试将有助于阐明这些功能的正确用法。

#2


8  

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

#3


0  

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”库保持一致。

#1


66  

How @strongify works

After @strongify is called, self will have a different pointer address inside the block than it will outside the 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警告,它会“在局部变量影响另一个局部变量时发出警告。”)值得阅读并理解这些函数的实现。因此即使名称相同,也将它们视为单独的强引用。

Using @strongify in your code

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将引用本地堆栈变量。当它们定义的范围结束时(只要您不将它们存储到属性或在嵌套块中使用它们),它们通常会被销毁。因此,根据您展示的代码,您只需在//自引用#1之后使用它。

See Also

Reading the unit test covering @weakify and @strongify will help clarify the correct usage of these functions.

阅读涵盖@ weakify和@strongify的单元测试将有助于阐明这些功能的正确用法。

#2


8  

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

#3


0  

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”库保持一致。