递归阻塞并保留ARC中的循环

ver*_*rec 20 recursion objective-c objective-c-blocks automatic-ref-counting retain-cycle

EDIT2:

不.建议的答案是关于异步调用.我想要并需要同步调用,就像在普通的标准递归调用中一样.

编辑:

__unsafe_unretained void (^unsafe_apply)(UIView *, NSInteger) ;
Run Code Online (Sandbox Code Playgroud)

在没有警告或错误的情况下编译,它在运行时失败,并将NULL存储到unsafe_apply中.

不过这个:

- (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) ;
}
Run Code Online (Sandbox Code Playgroud)

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

但这太丑了!


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

- (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) ;
}
Run Code Online (Sandbox Code Playgroud)

我收到这个警告:

/Users/verec/Projects/solotouch/SoloTouch/BubbleMenu.m:551:42: Block pointer variable 'apply' is uninitialized when captured by block

如果我应用建议的修复: Maybe you meant to use __block 'apply'

void (^__block apply)(UIView *, NSInteger) = ^(UIView * view, NSInteger level) {
Run Code Online (Sandbox Code Playgroud)

然后我得到:/Users/verec/Projects/solotouch/SoloTouch/BubbleMenu.m:554:13: Capturing 'apply' strongly in this block is likely to lead to a retain cycle

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

__weak typeof (apply) wapply = apply ;
if (wapply) {
    __strong typeof (wapply) sappy = wapply ;
    wapply(subview, 1+level) ;
}
Run Code Online (Sandbox Code Playgroud)

但事情变得更糟,变成了错误.

我最终得到了这个:

__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) ;
Run Code Online (Sandbox Code Playgroud)

任何人都有一个更好的解决方案,我可以从块内做任何事情,而不是像我在这里做的那样可靠地修补它?

注意那些SO 问题是关于捕获的self,那些SO 问题没有任何令人满意的答案.

new*_*cct 61

您需要捕获__block变量,因为块__block在创建时按值捕获非变量,并且在创建块之后进行赋值.

在ARC中,块保留__block对象指针类型的变量(通常所有变量都是隐式的__strong).因此,如果块捕获__block指向自身的变量,则会创建保留周期.解决方案是让它捕获一个弱引用.在支持的OS版本中__weak,__weak应该使用而不是__unsafe_unretained.

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

因此,您需要两个变量,一个弱,一个强.在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) ;
Run Code Online (Sandbox Code Playgroud)

  • @Drux:改变了作业顺序 (4认同)