递归块中的ARC行为 [英] ARC behavior within a recursive block

查看:91
本文介绍了递归块中的ARC行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经完成了以下两个实用程序功能:

I've made these two utility funcions:

+ (void)dispatch:(void (^)())f afterDelay:(float)delay {
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay*NSEC_PER_SEC)),
                    dispatch_get_main_queue(),
                    f);
  }

+ (void)dispatch:(void (^)())f withInterval:(float)delay {
    void (^_f)() = nil; // <-- A
    _f = ^{
        f();
        [self dispatch:_f afterDelay:delay]; // <-- B
    };
    [self dispatch:_f afterDelay:delay];
}

想法是您可以致电:

[自派遣:阻止,延迟时间:延迟]; -在特定时间后执行块

[self dispatch:block afterDelay:delay]; - to get a block executed after a specific time

[自我调度:阻止,间隔:延迟]; -获取定期执行的块

[self dispatch:block withInterval:delay]; - to get a block executed periodically

好吧,如果我直接调用 dispatch:withInterval:,它将在运行时创建错误,因为当程序尝试在 B 处执行该行时 _f 的值将为 nil ;进而发生这种情况是因为 _f A 处引用了 _f 的值.

Ok now, if I call dispatch:withInterval:, as it is, it will create an error at runtime because when the program tries to execute the line at B the value of _f will be nil; and that in turn happens because _f holds a reference to the value of _f at A.

如果我将 A 更改为:

__block void (^_f)() = nil;

为此,我大力引用了 _f ,因此当代码达到 B 时, _f 的值即为最终值分配给它的值.问题是我陷入了一个保留周期.

and with this I'm making a strong reference to _f, so when the code reaches B the value of _f is the final value that was assigned to it. The problem with this is that I'm incurring into a retain cycle.

最后,我可以将 A 更改为:

Finally, I can change A to be:

__block void (^_f)() __weak = nil;

,并且应该处理这两个问题,但是我发现当代码到达 B 时, _f 的值再次出现 nil ,因为在评估时, _f 已被释放.

and that should take care of both issues, however I've found that when the code reaches B the value of _f is again nil because, at the time it gets evaluated, _f has already been deallocated.

我有几个问题:

  • 在最后一种情况下,为什么 _f 被释放?如何告诉ARC至少在下一次调度调用之前保留该块?
  • 编写这些功能的最佳方式(与ARC兼容)是什么?
  • On the last scenario, why does _f get deallocated? How do I tell ARC to retain the block at least until the next dispatch call?
  • What would be the best (and ARC-compliant) way to write these functions?

感谢您的时间.

推荐答案

如何告诉ARC至少在下一次调度调用之前保留该块?

How do I tell ARC to retain the block at least until the next dispatch call?

我想说的是,通过与__block一起使用的方法.

I would say, by the method you use with __block.

问题是我陷入了保留周期.

The problem with this is that I'm incurring into a retain cycle.

我不明白为什么会是一个问题.您希望您的计时器无限期启动,对吧?这意味着与之关联的对象也必须永远存在.只要您要分派该块,无论如何它都会被GCD保留,但是拥有额外的引用似乎并没有受到伤害.

I'm not getting why that would be a problem. You want your timer to fire indefinitely, right? This means that objects associated with it have to live forever as well. As long as you're dispatching the block, it is retained by GCD anyway, but having an additional reference doesn't seem to hurt.

如果将来某个时候决定取消计时器,则可以通过设置_f = nil来取消.这将打破保留周期.

If, at a some point in the future, you decide to cancel the timer, you do so by setting _f = nil. This will break the retain cycle.

编写这些功能的最佳方式是什么(和ARC兼容)?

What would be the best (and ARC-compliant) way to write these functions?

好吧,最好的方法是使用NSTimer. 但是我确实认为学习如何使用GCD很有趣.令人高兴的是,Apple有此处的计时器示例.

Well, the best way would be to use NSTimer. But I do think it is interesting to learn how to use GCD. Happily, Apple has a timer example here.

好的,但是,每次调用_f时,对_f的引用不会增加吗?

Ok but, doesn't the reference to _f get incremented each time _f is called?

让我们看一下__block的工作方式.系统要做的是在堆上创建一个全局变量,然后向该内存传递一个 reference (例如,值为A的指针)到您的块(例如,位于内存值B).

Let's take a look at how __block works. What the system does, is creating a global variable on a heap and passing a reference (say, a pointer with value A) to that memory to your block (say, located at memory value B).

因此,您在地址A处有一些内存,该内存引用了地址B处的内存,反之亦然.如您所见,这里每个对象的保留计数为1;好的,GCD也可以保留,但是保留数量是恒定的,没有理由增加.

So, you have some memory at address A that references memory at address B, and vice versa. As you see, here each object has a retain count of 1; well, GCD also retains, but this retain count is constant and has no reason to be increasing.

您可以从其他位置将_f设为空,然后在GCD完成该块之后,保留计数将变为0.

You can null _f from some other place and then after GCD finishes the block the retain count goes to 0.

当我使用__weak时为什么会释放它?

why does it get deallocated when I use __weak?

我们已经看到,有两个因素会影响地址B上对象的ARC计数:GCD和变量_f.如果将_f设为弱,则在分配它后,您的块仍然没有来自_f的保留计数,并且它也没有来自B行的计数,因为您尚未实际运行该块.因此,它立即被释放.

As we've seen, there are two things that affect the ARC count of object at address B: GCD and variable _f. If you make _f weak, then after assignment to it, your block still has no retain count from _f, and it has no count from line B since you haven't actually run the block. Thus it gets immediately deallocated.

注意. 这就是ARC的优点:您每次都会得到这种行为,在这里我们可以从逻辑上跟踪所有发生的事情并推断出原因.使用垃圾收集器,有时会释放该块,有时不会释放该块,这使调试此问题变得很困难.

Note. That's the beauty of ARC: you will get this behavior every time, and here we can follow all that happens logically and deduce the reason. With garbage collector, this block would be sometimes deallocated and sometimes not, making debugging this problem a hell.

这篇关于递归块中的ARC行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆