iOS阻止和对自我的强/弱引用

Mas*_*son 40 objective-c ios objective-c-blocks

我有一个关于iOS中块的自我强弱参考的问题.我知道在块内引用self的正确方法是在块外创建一个弱引用,然后在块内强引用该弱引用,如下所示:

__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
    typeof(self) strongSelf = weakSelf;
    NSLog(@"%@", strongSelf.someProperty);
});
Run Code Online (Sandbox Code Playgroud)

但是,如果你有嵌套块会发生什么?一组参考文献足够吗?或者您是否需要为每个区块设置新组?例如,以下哪项是正确的?

这个:

__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
    typeof(self) strongSelf = weakSelf;
    NSLog(@"%@", strongSelf.someProperty);
    dispatch_async(dispatch_get_main_queue(), ^ {
        strongSelf.view.frame = CGRectZero;
    });
});
Run Code Online (Sandbox Code Playgroud)

或这个:

__weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
        typeof(self) strongSelf = weakSelf;
        NSLog(@"%@", strongSelf.someProperty);
        __weak typeof(strongSelf) weakSelf1 = strongSelf;
        dispatch_async(dispatch_get_main_queue(), ^ {
            typeof(strongSelf) strongSelf1 = weakSelf1;
            strongSelf1.view.frame = CGRectZero;
        });
    });
Run Code Online (Sandbox Code Playgroud)

非常感谢任何信息或解释!

Jef*_*ley 52

您不需要制作两组弱引用.你想用块来避免的是一个保留周期 - 两个对象让对方不必要地活着.

如果我有一个具有此属性的对象:

@property (strong) void(^completionBlock)(void);
Run Code Online (Sandbox Code Playgroud)

我有这个方法:

- (void)doSomething
{
    self.completionBlock = ^{
        [self cleanUp];
    };

    [self doLongRunningTask];
}
Run Code Online (Sandbox Code Playgroud)

当我将它存放在completionBlock房产中时,该区块将保持活着状态.但是因为它self在块内部引用,所以块将保持self活着直到它消失 - 但这不会发生,因为它们都是相互引用的.

在这个方法中:

- (void)doSomething
{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [self cleanUp];
    }];

    [self doLongRunningTask];
}
Run Code Online (Sandbox Code Playgroud)

你不需要做一个弱引用self.该块将保持self活动状态,因为它self从内部引用,但由于我们所做的只是将块移交给[NSOperationQueue mainQueue],self所以不会使块保持活动状态.

希望这可以帮助.


Rob*_*Rob 23

两种结构都很好.这取决于你的意图.如果对象是(a)在外部块开始之后释放但是(b)在内部块在主队列上开始之前,你想要发生什么?如果您不希望它保留在这种情况下(我可能会猜测是您的意图,因为您首先要进行此weakSelf练习),然后使用您的最后一个示例,其中您有第二个弱指针.否则你可以使用你的另一个例子.

话虽如此,有几点意见:

  1. 你必须首先使用这种weakSelf模式,这不是一个放弃的结论.有些人错误地认为他们必须使用这种weakSelf模式来避免强烈的参考周期(也就是保留周期).但是这个代码示例并不构成强大的参考周期.它只是在调度代码执行时保留对象,这是一个非常不同的考虑因素.

    事实上,有时你需要/想要那个.有时候你没有.这取决于您正在解决的业务问题.当然,你经常不希望它强烈引用self,在这种情况下,weakSelf模式是完全有意义的.但情况并非总是如此.

    但我的观点是,你不应该追求这种weakSelf模式(至少在这种dispatch_async情况下),以避免强大的参考周期.没有这样的循环.这是一个问题,你有一个块变量(例如一些completionHandler块).在这种情况下,weakSelf模式至关重要.但不是在这里.

  2. 但是让我们考虑一下您不希望self保留的情况.然后是一个问题,你是否希望首先继续发送代码.如果没有,也许您应该使用具有可取消操作的操作队列而不是GCD.

    例如,我很惊讶人们在一些后台网络请求运行时是否经常会保留视图控制器时会感到痛苦,但是不要担心他们是否应该首先取消该后台网络请求.通常,后者是一个更重要的设计考虑因素(例如,您下载的PDF或图像占用的视频控制器将占用更多的系统资源(内存和网络带宽)).

  3. 但是我们假设(a)你真的希望调度的代码继续执行,但(b)你不想保留self.(这似乎是一种罕见的情况,但它是你所问的那个,所以让我们继续追求.)最后一个问题是你是否需要你的strongSelf构造.在你的情况下,你只需要调用一个方法self,你不需要为这个strongSelf结构而烦恼.只有当你要顺从ivars或者需要避免竞争条件时,这才是至关重要的.但是,在这个例子中,假设发送给nil对象的消息什么都不做,你在技术上通常根本不需要担心这个strongSelf构造.

别误会我的意思.让一个人围绕这个weakSelf模式,以及strongSelf有时伴随它的嵌套模式是很好的.我只是建议理解何时真正需要这些模式.而且我认为GCD与可取消的选择NSOperation往往是一个更为关键但往往被忽视的问题.