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

Mik*_*ike 42 objective-c ios 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];
        }];
    }
}
Run Code Online (Sandbox Code Playgroud)

我的理解是,如果我想做异步HTTP请求之类的事情,并且在完成处理程序引用self内部,比如[self reloadData],我不需要对强/弱做任何事情,因为请求块本身没有保留完成块,那里保留周期没有问题.在上面的代码示例中,我认为#5是保留周期不是问题的情况.

主要关注的是所有将块作为属性/ init参数的对象,这些对象内部保留在块属性上.在objectWithCompletionHandler方法内部,someObject作为实例变量保存在completionHandler块中,有多个对self的引用,我知道这会引起泄漏.我的主要问题是在这种情况下,您需要如何处理weakifystrongify使其"更安全"?一个@weakify和@strongify调用是否足够,如下所示:

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

    _someObjectInstanceVar = [Object objectWithCompletionHandler:^{
        @strongify(self);
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

Aar*_*ger 80

怎么@strongify工作

@strongify调用之后,块内部self将具有与块外部不同的指针地址.那是因为每次都声明一个新的局部变量.(这就是为什么它会抑制警告,它会"在局部变量影响另一个局部变量时发出警告.")值得阅读并理解这些函数的实现.因此,即使名称相同,也将它们视为单独的引用.@strongifyself-Wshadowstrong

使用@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…
}
Run Code Online (Sandbox Code Playgroud)

但是,请记住,在您第一次使用之后@strongify,self将引用本地堆栈变量.当它们定义的范围结束时(只要您不将它们存储到属性或在嵌套块中使用它们),它们通常会被销毁.所以根据你展示的代码,你只需要它// self reference #1.

也可以看看

阅读单元测试封面@weakify,@strongify将有助于阐明这些功能的正确用法.


car*_*ess 8

要回答关于块的每个嵌套级别中的多个弱化/强化实例是否有效的问题,请回答是.但是没有必要这样做,因为你的第一个@strongify定义已经为你的块的所有内部范围(以及嵌套在它中的块)定义了self.

但是,假设您的块具有不同的生命周期,您可能希望为每个嵌套块添加@strongify,以确保它们都将自己的保留周期保留到其内部范围.

这是解释这种情况的github问题线程:https: //github.com/jspahrsummers/libextobjc/issues/45