Рекурсивный цикл блокировки и сохранения в ARC - программирование
Подтвердить что ты не робот

Рекурсивный цикл блокировки и сохранения в ARC

EDIT2:

Нет. Предлагаемый ответ касается асинхронных вызовов. Я хочу и нуждаюсь в синхронных вызовах, например, в обычном, стандартном рекурсивном вызове.

EDIT:

а

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

компилируется без предупреждения или ошибок, он не работает во время выполнения с 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) ;
}

компилируется без предупреждений, но что более важно, на самом деле выполняется.

Но это настолько уродливое!


рассмотрим (раскраски иерархии представлений, для раскрытия цели...):

- (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

Если я применил предложенное исправление: 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

Я пробовал различные способы подделать код и избавиться от этих предупреждений

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

У кого-то есть лучшее решение, где я мог бы делать все изнутри блока, а не отвратительно возвращать его, как мне приходилось делать здесь?

Примечание. Эти qaru.site/info/327663/... Questions относятся к захвату self, а те qaru.site/info/327663/... questions не имеют удовлетворительного ответа.

4b9b3361

Ответ 1

Вам нужно записать переменную __block, потому что блокируют захват не __block переменных по значению при их создании, а назначение происходит после создания блока.

В ARC, __block переменные типа указателя объекта (как правило, все переменные неявно __strong) сохраняются блоком. Поэтому, если блок захватывает переменную __block, указывающую на себя, она создаст цикл сохранения. Решение заключается в том, чтобы улавливать слабую ссылку. В версиях ОС, поддерживающих __weak, вместо __unsafe_unretained следует использовать __weak.

Однако, если единственной ссылкой на блок была переменная __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) ;

Ответ 2

Чтобы избежать предупреждений ARC и основываться на ответе @newacct, я обнаружил, что настройка слабого блока внутри сохраненного блока работает:

//a block definition
typedef void (^CompletionType)(NSDictionary * __nullable response, NSError * __nullable error);


//a block calling itself without the ARC retain warning
__block CompletionType completionBlock = nil;
__block __weak CompletionType weakCompletionBlock = nil;
completionBlock = ^(NSDictionary *response, NSError *error) {
    weakCompletionBlock = completionBlock;

        weakCompletionBlock();

    });
};

Ответ 3

Ответ - нет.

Мы не можем выглядеть лучше, чем использовать квалификатор __block.

__block void(^strawberryFields)();
strawberryFields = ^{ strawberryFields(); };
strawberryFields();

Благодаря Bill Bumgarner о блоках.


EDIT:

__block __weak void(^strawberryFields)();

представляется предпочтительным способом в ARC.