简体   繁体   English

阻止在块中,__ weak self

[英]Block in block, with __weak self

I'm trying to figure out if I do this right: 我想知道我是否做得对:

If I have one block, I'll do this: 如果我有一个区块,我会这样做:

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

    [weakSelf doSomething];

} afterDelay:delay];

But what happens if there's a block in a block? 但是如果块中存在块,会发生什么? Would this be correct? 这是正确的吗?

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

    [weakSelf doSomething];

    [self performBlock:^{

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

} afterDelay:delay];

Also, in the function below, do I need to use [block copy]? 另外,在下面的功能中,我是否需要使用[块复制]?

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

Rather than implementing -performBlock:afterDelay: , just use dipatch_after() . 而不是实现-performBlock:afterDelay:只需使用dipatch_after() Among other things, that's not a message delivered to an object, so there's no question of what receiver to target it at. 除此之外,这不是传递给对象的消息,因此毫无疑问接收器的目标是什么。

Actually, there's no memory management issue here at all. 实际上,这里根本没有内存管理问题。 One typically only needs to do a "weak self" approach when an object retains a block and the block (perhaps implicitly) retains that same object. 当一个对象保留一个块并且该块(可能是隐含的)保留该同一个对象时,通常只需要执行“弱自我”方法。 However, the object is not retaining the block. 但是,该对象不保留该块。 It is being retained by the framework until the -performSelector:withObject:afterDelay: fires, but that's not a retain cycle. 它被框架保留,直到-performSelector:withObject:afterDelay: fires,但这不是一个保留周期。

If there were a retain cycle, then you should not reference self in the blocks. 如果存在保留周期,则不应在块中引用self So, your nested case is wrong in invoking a message on self rather than weakSelf . 所以,你的嵌套案例在调用self而不是weakSelf上的消息时是错误的。

Finally, yes, you do need [block copy] whenever you are keeping a block after execution leaves the scope of its declaration or if you pass it to non-block-specific API that does. 最后,是的,只要在执行后保留一个块离开其声明的范围或者将其传递给非特定于块的API,就需要[block copy] That is, you don't need to copy a block when you pass it to, say, dispatch_async() because that's a block-aware API that knows to make its own copy as necessary. 也就是说,当你将它传递给dispatch_async()时,你不需要复制一个块,因为这是一个块知道的API,它知道在必要时制作自己的副本。 But -performSelector:withObject:afterDelay: is not block-aware. 但是-performSelector:withObject:afterDelay:不是块感知的。 It just treats its argument as a generic object and retains it. 它只是将其参数视为通用对象并保留它。 So, you do have to copy the block when passing it to that. 因此,您必须在将块传递给它时复制该块。

In this case (below) use just strong self , because the block is copied just for those few seconds. 在这种情况下(下面) 使用强大的 self ,因为块被复制只是几秒钟。 And usually if you want the self to perform block, you want to it to stay alive until that time, so strong reference is perfectly okay. 通常如果你想要self执行阻止,你希望它保持活着直到那个时候,所以强大的参考是完全可以的。

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

Block inside a block? 块内阻塞? In your case those two block are just delayed one-shot blocks, so the same as above, use strong. 在你的情况下,这两个块只是延迟一次性块,所以与上面相同,使用强。 But there are differences between blocks. 但是块之间存在差异。 If you store the block for longer time , maybe for multiple invocations you should avoid retain-cycles. 如果您将块存储较长时间 ,也许对于多次调用,您应该避免保留周期。

Example: 例:

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

This may cause retain-cycle. 可能会导致保留周期。 In fact it depends on how the block is used. 实际上,它取决于块的使用方式。 We see that the block is stored (copied) in property for later use. 我们看到该块被存储(复制)在属性中供以后使用。 However, you can prevent the retain-cycles by nullifying block that will not be used any more. 但是,您可以通过使不再使用的块无效来阻止保留周期。 In this case: 在这种情况下:

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

When using ARC, you don't have to copy blocks yourself. 使用ARC时,您不必自己复制块。 There were bugs in early versions after blocks were added, but now the compiler under ARC knows when to copy blocks. 在添加块之后,早期版本中存在错误,但现在ARC下的编译器知道何时复制块。 It is clever enough to copy it in this case: 在这种情况下复制它很聪明:

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

One the most important thing to understand about blocks is that they capture a piece of code (including values) in an abstract entity that can be manipulated as an atomic object (keep it somewhere, pass it, copy, etc...). 理解块最重要的一点是它们在抽象实体中捕获一段代码(包括值),可以作为原子对象进行操作(将其保存在某处,传递,复制等等)。 Actually it is implemented in a way that guarantee that by default your block will remain valid and executable safely later. 实际上,它的实现方式可以保证默认情况下,您的块将保持有效并在以后安全地执行。

Then capturing and retaining the required dependencies inside the block is necessary. 然后捕获并保留块内所需的依赖项是必要的。

Unfortunately, in some cases (quite often actually) the block is retained by the instance that creates it and it retains itself that instance. 不幸的是,在某些情况下(实际上经常是),块由创建它的实例保留,并且它自己保留该实例。 This is called a retain loop and makes your object and your block impossible to dealloc unless you break one of the retaining relation yourself. 这被称为保留循环,除非你自己打破其中一个保留关系,否则你的对象和你的块不可能被释放。 This can happen if you reference your block with an instance variable for example and you don't nillify it manually. 例如,如果您使用实例变量引用块,并且未手动将其枚举,则会发生这种情况。

This is probably the main issue with blocks especially because sometime, you don't know that your block retains your self instance (NSAssert within your block for example). 这可能是块的主要问题,特别是因为有时候,你不知道你的块保留了你的自我实例(例如你的块中的NSAssert)。 Then: 然后:

  • If you execute your block immediately and release it (use your block with dispatch release it after execution) there is no risk since you are sure your object referenced by self still exist. 如果你立即执行你的块并释放它(使用你的块并在执行后释放它就发送它)没有风险,因为你确定你自己引用的对象仍然存在。

  • But if the execution is delayed it is important to retain your object within your block. 但是如果执行被延迟,那么将对象保留在块中是很重要的。 But in that case your object should not retain your block to avoid a retain loop (A retains B and B retains A). 但在这种情况下,你的对象不应该保留你的块以避免保留循环(A保留B和B保留A)。 If you define and optionally reference your block in the private scope of method it is perfect. 如果您在方法的私有范围内定义并可选地引用块,则它是完美的。

  • About copy. 关于副本。 Yes it can be safer to use copy if your block in passed as a method argument to be sure you have a clean exclusive block in this scope with a +1 retainCount. 是的,如果您的块作为方法参数传递以确保在此范围内具有+1 retainCount的干净独占块,则使用副本会更安全。 But maybe ARC already do it for you. 但也许ARC已经为你做了。 Not sure about that. 不确定。 For example it performWithSelector seems to do it for free, then copy is not dangerous. 例如,performWithSelector似乎是免费的,然后复制并不危险。 Just a useless. 只是一个无用的。 Sometime the compiler can optimise that by removing it but it has to be checked. 有时编译器可以通过删除它来优化它,但必须进行检查。

I usually do this: 我经常这样做:

__unsafe_unretained __block id blockSelf = self;

and then use it in my blocks no issues. 然后在我的块中使用它没有问题。

So in your case: 所以在你的情况下:

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

Also to make your life a tad easier - make a utilities class and put this in the header 还要让你的生活更轻松 - 制作一个实用程序类并将其放在标题中

void RunAfterDelay(NSTimeInterval delayInSeconds, dispatch_block_t block);

and then this in the .m file 然后在.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);
}

Import the utilities into your prefix and you can go: 将实用程序导入前缀,您可以:

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

I find it a bit nicer to read than the verbose default ones. 我发现阅读比详细的默认值更好。

Hope this helps :) 希望这可以帮助 :)

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

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