阻止在块中,__ weak self

时间:2013-03-16 11:10:02

标签: ios objective-c xcode block

我想知道我是否做得对:

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

__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();
}

4 个答案:

答案 0 :(得分:5)

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

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

如果存在保留周期,则不应在块中引用self。因此,在self而不是weakSelf上调用邮件时,您的嵌套案例是错误的。

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

答案 1 :(得分:5)

在这种情况下(下方)仅使用强大的 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];

答案 2 :(得分:0)

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

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

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

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

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

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

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

答案 3 :(得分:-3)

我通常这样做:

__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];
    })
});

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

希望这会有所帮助:)