递归块并在ARC中保留循环 [英] recursive block and retain cycles in ARC

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

问题描述

不.建议的答案是关于 async 调用.我要需要同步调用,例如在普通的标准递归调用中.

No. The suggested answer is about async calls. I want & need synchronous calls, like in a normal, standard recursive call.

同时

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

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

compiles without warning or errors, it fails at runtime with a NULL stored into 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) ;
}

编译时不会发出警告,但更重要的是,它实际上可以运行.

compiles without warnings, but more importantly, actually runs.

但这太太丑了

考虑(为视图层次着色,以显示目的……):

- (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 pointer variable 'apply' is uninitialized when captured by block

/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'

If I apply the suggested fix: Maybe you meant to use __block 'apply'

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

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

I then get: /Users/verec/Projects/solotouch/SoloTouch/BubbleMenu.m:554:13: Capturing 'apply' strongly in this block is likely to lead to a retain cycle

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

I tried various ways to tamper with the code and get rid of those warnings

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

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

But things just get worse, turning into errors.

我最终得到了这个

__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) ;

任何人都有更好的解决方案,在这里我可以在块内完成所有操作,而不必像在这里那样卑鄙地后补丁?

Anyone has a better solution, where I could do everything from within the block and not hideously back patch it as I had to do here?

请注意那些 SO SO 问题没有令人满意的答案.

Note Those SO Questions are about capturing self and those SO questions don't have any satisfactory answer.

推荐答案

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

You need to capture a __block variable, because blocks capture non-__block variables by value when they are created, and the assignment happens after the block has been created.

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

In ARC, __block variables of object pointer type (generally all variables are implicitly __strong) are retained by the block. So if the block captures a __block variable pointing to itself, it would create a retain cycle. The solution is to have it capture a weak reference. In versions of the OS that support __weak, __weak should be used instead of __unsafe_unretained.

但是,如果对该块的 only 引用是一个__weak变量,则不会有对该块的强引用,这意味着可以将其释放.为了使用该块,它必须具有强大的参考意义以保持其存在.

However, if the only reference to the block was a __weak variable, there would be no strong references to the block, which means it can be deallocated. In order to use the block, it must have a strong reference to keep it around.

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

Therefore, you need two variables, one weak and one strong. The proper way to do it in ARC is:

__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) ;

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

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