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

时间:2015-02-03 17:44:20

标签: ios objective-c libextobjc

我开始将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的引用,我知道这会引起泄漏。我的主要问题是在这种情况下,您需要如何处理weakifystrongify以使其“更安全”?一个@weakify和@strongify调用是否足够,如下所示:

- (void)someMethod {
    @weakify (self);

    _someObjectInstanceVar = [Object objectWithCompletionHandler:^{
        @strongify(self);
    ...
}

以上@strongify(self)引用是否足以用于自引用#1,2,3和4,或者我必须(甚至可以工作)获得一个新的弱/强引用在sendAWithID方法和嵌套reloadData

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

3 个答案:

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