繁体   English   中英

阻止在块中,__ weak self

[英]Block in block, with __weak self

我想知道我是否做得对:

如果我有一个区块,我会这样做:

__weak MyClass *weakSelf = self;  
[self performBlock:^{                 //<< Should I use self, or weakSelf here?

    [weakSelf doSomething];

} afterDelay:delay];

但是如果块中存在块,会发生什么? 这是正确的吗?

__weak MyClass *weakSelf = self;
[self performBlock:^{

    [weakSelf doSomething];

    [self performBlock:^{

        [weakSelf doSomething]; 
    } afterDelay:1.0f];

} afterDelay:delay];

另外,在下面的功能中,我是否需要使用[块复制]?

- (void)performBlock:(void (^)(void))block afterDelay:(float)delay
{
    if (block)
    {
        if (delay > 0)
        {
            [self performSelector:@selector(executeBlockAfterDelay:) withObject:[block copy] afterDelay:delay];
        }
        else
        {
            [self executeBlockAfterDelay:[block copy]];
        }
    }
}

- (void)executeBlockAfterDelay:(void(^)(void))block
{
    if (block)
        block();
}

而不是实现-performBlock:afterDelay:只需使用dipatch_after() 除此之外,这不是传递给对象的消息,因此毫无疑问接收器的目标是什么。

实际上,这里根本没有内存管理问题。 当一个对象保留一个块并且该块(可能是隐含的)保留该同一个对象时,通常只需要执行“弱自我”方法。 但是,该对象不保留该块。 它被框架保留,直到-performSelector:withObject:afterDelay: fires,但这不是一个保留周期。

如果存在保留周期,则不应在块中引用self 所以,你的嵌套案例在调用self而不是weakSelf上的消息时是错误的。

最后,是的,只要在执行后保留一个块离开其声明的范围或者将其传递给非特定于块的API,就需要[block copy] 也就是说,当你将它传递给dispatch_async()时,你不需要复制一个块,因为这是一个块知道的API,它知道在必要时制作自己的副本。 但是-performSelector:withObject:afterDelay:不是块感知的。 它只是将其参数视为通用对象并保留它。 因此,您必须在将块传递给它时复制该块。

在这种情况下(下面) 使用强大的 self ,因为块被复制只是几秒钟。 通常如果你想要self执行阻止,你希望它保持活着直到那个时候,所以强大的参考是完全可以的。

[self performBlock:^{
    [self doSomething]; // strong is OK
} afterDelay:delay];

块内阻塞? 在你的情况下,这两个块只是延迟一次性块,所以与上面相同,使用强。 但是块之间存在差异。 如果您将块存储较长时间 ,也许对于多次调用,您应该避免保留周期。

例:

self.callback = ^{
    [self doSomething]; // should use weakSelf
};

可能会导致保留周期。 实际上,它取决于块的使用方式。 我们看到该块被存储(复制)在属性中供以后使用。 但是,您可以通过使不再使用的块无效来阻止保留周期。 在这种情况下:

self.callback(); //invoke
self.callback = nil; //release

使用ARC时,您不必自己复制块。 在添加块之后,早期版本中存在错误,但现在ARC下的编译器知道何时复制块。 在这种情况下复制它很聪明:

[self performSelector:@selector(executeBlockAfterDelay:) withObject:block afterDelay:delay];

理解块最重要的一点是它们在抽象实体中捕获一段代码(包括值),可以作为原子对象进行操作(将其保存在某处,传递,复制等等)。 实际上,它的实现方式可以保证默认情况下,您的块将保持有效并在以后安全地执行。

然后捕获并保留块内所需的依赖项是必要的。

不幸的是,在某些情况下(实际上经常是),块由创建它的实例保留,并且它自己保留该实例。 这被称为保留循环,除非你自己打破其中一个保留关系,否则你的对象和你的块不可能被释放。 例如,如果您使用实例变量引用块,并且未手动将其枚举,则会发生这种情况。

这可能是块的主要问题,特别是因为有时候,你不知道你的块保留了你的自我实例(例如你的块中的NSAssert)。 然后:

  • 如果你立即执行你的块并释放它(使用你的块并在执行后释放它就发送它)没有风险,因为你确定你自己引用的对象仍然存在。

  • 但是如果执行被延迟,那么将对象保留在块中是很重要的。 但在这种情况下,你的对象不应该保留你的块以避免保留循环(A保留B和B保留A)。 如果您在方法的私有范围内定义并可选地引用块,则它是完美的。

  • 关于副本。 是的,如果您的块作为方法参数传递以确保在此范围内具有+1 retainCount的干净独占块,则使用副本会更安全。 但也许ARC已经为你做了。 不确定。 例如,performWithSelector似乎是免费的,然后复制并不危险。 只是一个无用的。 有时编译器可以通过删除它来优化它,但必须进行检查。

我经常这样做:

__unsafe_unretained __block id blockSelf = self;

然后在我的块中使用它没有问题。

所以在你的情况下:

__unsafe_unretained __block MyClass *blockSelf = self;
[self performBlock:^{
    [weakSelf doSomething];
    [self performBlock:^{
        [weakSelf doSomething]; 
    } afterDelay:1.0f];
} afterDelay:delay];

还要让你的生活更轻松 - 制作一个实用程序类并将其放在标题中

void RunAfterDelay(NSTimeInterval delayInSeconds, dispatch_block_t block);

然后在.m文件中

void RunAfterDelay(NSTimeInterval delayInSeconds, dispatch_block_t block)
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC), dispatch_get_main_queue(), block);
}

将实用程序导入前缀,您可以:

__unsafe_unretained __block MyClass *blockSelf = self;
RunAfterDelay(1.0f,^{
    [blockSelf doSomething];
    RunAfterDelay(delay,^{
        [blockSelf doSomething];
    })
});

我发现阅读比详细的默认值更好。

希望这可以帮助 :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM