objective-c - ARC 中的递归 block 和保留循环

标签 objective-c recursion automatic-ref-counting objective-c-blocks retain-cycle

编辑2:

没有。建议的答案是关于异步 调用。我想要并且需要同步调用,就像在正常的标准递归调用中一样。

编辑:

同时

__unsafe_unretained void (^unsafe_apply)(UIView *, NSInteger) ;

编译时没有警告或错误,它在运行时失败并在 unsafe_apply 中存储 NULL。

但是这样:

- (void) applyToView: (UIView *) view {

    UIColor * (^colorForIndex)(NSInteger) = ^(NSInteger index) {
        return [UIColor colorWithHue: ((CGFloat) index / 255.0f)
                          saturation: 0.5f
                          brightness: 0.5f
                               alpha: 1.0f] ;
    } ;

    void (^applyColors) (UIView *, NSInteger index) = ^(UIView * view, NSInteger index) {
        view.backgroundColor = colorForIndex(index) ;
    } ;

    void (^__block recurse_apply)(UIView *, NSInteger) ;

    void (^apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {
        applyColors(view, level) ;
        [view.subviews enumerateObjectsUsingBlock:^(UIView * subview, NSUInteger idx, BOOL *stop) {
            recurse_apply(subview, 1+level) ;
        }] ;
    } ;

    recurse_apply = apply ;

    apply(view, 0) ;
}

编译没有警告,但更重要的是,实际运行。

但这太丑了!


考虑(为 View 层次结构着色,用于暴露目的...):

- (void) applyToView: (UIView *) view {

    UIColor * (^colorForIndex)(NSInteger) = ^(NSInteger index) {
        return [UIColor colorWithHue: ((CGFloat) (index * 10.0f) / 255.0f)
                          saturation: 0.5f
                          brightness: 0.5f
                               alpha: 1.0f] ;
    } ;

    void (^applyColors) (UIView *, NSInteger index) = ^(UIView * view, NSInteger index) {
        view.backgroundColor = colorForIndex(index) ;
    } ;

    void (^apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {
        applyColors(view, level) ;
        [view.subviews enumerateObjectsUsingBlock:^(UIView * subview, NSUInteger idx, BOOL *stop) {
            apply(subview, 1+level) ;
        }] ;
    } ;

    apply(view, 0) ;
}

我收到这个警告:

/Users/verec/Projects/solotouch/SoloTouch/BubbleMenu.m:551:42: block 指针变量“apply”在被 block 捕获时未初始化

如果我应用建议的修复:也许你打算使用 __block 'apply'

void (^__block apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {

然后我得到:/Users/verec/Projects/solotouch/SoloTouch/BubbleMenu.m:554:13: 在此 block 中强烈捕获“apply”可能会导致保留周期

我尝试了各种方法来篡改代码并摆脱那些警告

__weak typeof (apply) wapply = apply ;
if (wapply) {
    __strong typeof (wapply) sappy = wapply ;
    wapply(subview, 1+level) ;
}

但事情只会变得更糟,变成错误。

我最终得到了这个:

__unsafe_unretained void (^unsafe_apply)(UIView *, NSInteger) ;

void (^apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {
    applyColors(view, level) ;
    [view.subviews enumerateObjectsUsingBlock:^(UIView * subview, NSUInteger idx, BOOL *stop) {
        unsafe_apply(subview, 1+level) ;
    }] ;
} ;

unsafe_apply = apply ;

apply(view, 0) ;

谁有更好的解决方案,我可以在 block 内做所有事情,而不是可怕地回补丁,就像我必须在这里做的那样?

注意那些SO Questions是关于捕获 self 和那些 SO questions没有任何满意的答案。

最佳答案

您需要捕获一个__block 变量,因为 block 在创建时按值捕获非__block 变量,并且赋值发生在之后 区 block 已创建。

在ARC中,对象指针类型的__block变量(一般所有变量都是隐式__strong)被block保留。因此,如果 block 捕获指向自身的 __block 变量,它将创建一个保留循环。解决方案是让它捕获弱引用。在支持 __weak 的操作系统版本中,应使用 __weak 而不是 __unsafe_unretained

但是,如果对 block 的唯一引用是一个__weak 变量,则不会有对该 block 的强引用,这意味着它可以被释放。为了使用这个 block ,它必须有一个强引用来保持它。

因此,您需要两个变量,一弱一强。在 ARC 中执行此操作的正确方法是:

__block __weak void (^weak_apply)(UIView *, NSInteger) ;
void (^apply)(UIView *, NSInteger) ;
weak_apply = apply = ^(UIView * view, NSInteger level) {
    applyColors(view, level) ;
    [view.subviews enumerateObjectsUsingBlock:^(UIView * subview, NSUInteger idx, BOOL *stop) {
        weak_apply(subview, 1+level) ;
    }] ;
} ;

apply(view, 0) ;

关于objective-c - ARC 中的递归 block 和保留循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19884403/

相关文章:

objective-c - 实现自定义 View 的最佳方式

ios - 使用 Masonry 自动布局 UITableViewCell

iphone - UIImage 显示在具有指定字符串值的单元格中

ios - Objective-C 中的 init 实际上做了什么?

java - 实现一个如果所有参数都在范围内则返回 true 的方法

recursion - 循环/递归和递归本身有什么区别?

javascript - 如何递归生成斐波那契数列数组?

swift - 如何使用 ARC 在 Swift 中手动保留?

ios - 如何从 Xcode IOS 崩溃中查找与可变内存地址相关的代码?

ios - 在 Swift 中将函数分配给 block 时捕获语义?