我已经制作了这两个实用功能:
+ (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];
}
这个想法是你可以打电话:
[自我发送:阻止 afterDelay:延迟]; - 在特定时间后执行块
和
[自我发送:阻止 withInterval:延迟]; - 定期执行一个块
现在好了,如果我按原样调用 dispatch:withInterval:,它将在运行时创建错误,因为当程序尝试执行 B 时的行时 _f 的值为 nil ;而这又发生了,因为 _f 在 A 处对 _f 的值进行了引用。
如果我将 A 更改为:
,则可以修复此问题__block void (^_f)() = nil;
并且我正在强烈引用 _f ,所以当代码达到 B 时, _f 的值是最终的分配给它的值。这个问题是我正在进入一个保留周期。
最后,我可以将 A 更改为:
__block void (^_f)() __weak = nil;
且应该处理这两个问题,但是我发现当代码达到 B 时, _f 的值再次出现 nil 因为,在评估时, _f 已被取消分配。
我有几个问题:
感谢您的时间。
答案 0 :(得分:4)
如何告诉ARC至少在下次调度电话之前保留该块?
我会说,通过您使用__block
的方法。
问题在于我正在进入保留周期。
我不明白为什么那会是一个问题。你想让你的计时器无限期地开火,对吧?这意味着与之关联的对象也必须永远存在。只要您派遣该块,它仍然由GCD保留,但是有额外的参考似乎并没有受到伤害。
如果您将来某个时候决定取消计时器,请设置_f = nil
。这将打破保留周期。
编写这些函数的最佳(和ARC兼容)方法是什么?
嗯,最好的方法是使用NSTimer
。
但我确实认为 有趣的是学习如何使用GCD。令人高兴的是,Apple有a timer example here。
好的但是,每次调用_f时,对_f的引用是否都会增加?
让我们来看看__block是如何工作的。系统的作用是在堆上创建一个全局变量,并将引用(比如一个值为A的指针)传递给该块的内存(例如,位于内存值B)。
因此,您在地址A处有一些内存,它引用地址B处的内存,反之亦然。如您所见,此处每个对象的保留计数为1;好吧,GCD也保留,但这个保留计数是不变的,没有理由增加。
你可以从其他地方取消_f
,然后在GCD结束后,保留计数变为0。
为什么在使用__weak时会被释放?
正如我们所看到的,有两件事会影响地址B上对象的ARC计数:GCD和变量_f
。如果你使_f
弱,那么在分配给它之后,你的块仍然没有来自_f
的保留计数,并且它没有来自B行的计数,因为你实际上没有运行该块。因此它立即被解除分配。
注意:的 这就是ARC的美丽:你每次都会得到这种行为,在这里我们可以遵循逻辑上发生的一切并推断出原因。使用垃圾收集器时,这个块有时会被释放,有时也不会,这使得调试这个问题变得很糟糕。
答案 1 :(得分:1)
_f
需要一个强大的参考,因为否则在ARC中,分配给它的块可能会立即消失,因为没有强引用它。
同时,块需要访问指向自身的指针,并且,正如您所发现的,这必须使用__block
变量来完成。从块到自身的强引用将导致保留周期,因此这必须是弱引用。
因此,您需要两个变量,一个强,一个弱:
+ (void)dispatch:(void (^)())f withInterval:(float)delay {
__block __weak void (^_f_weak)() = nil; // a weak __block variable for the block to capture
void (^_f)() = nil; // a strong variable to hold the block itself
_f_weak = _f = ^{ // both variables will point to the block
f();
[self dispatch:_f_weak afterDelay:delay];
};
[self dispatch:_f afterDelay:delay];
}