Цикл рекурсии и прерывания блоков - программирование
Подтвердить что ты не робот

Цикл рекурсии и прерывания блоков

Чтобы лучше проиллюстрировать вопрос, рассмотрим следующую упрощенную форму рекурсии блоков:

__block void (^next)(int) = ^(int index) {
    if (index == 3) {
        return;
    }
    int i = index;
    next(++i);
};
next(0);

XCode (ARC-enabled) предупреждает, что "Capturing" next "сильно в этом блоке, вероятно, приведет к циклу сохранения".

Согласен.

Вопрос 1. Будет ли успешно сохранен цикл сохранения, установив сам блок на nil следующим образом:

__block void (^next)(int) = ^(int index) {
    if (index == 3) {
        next = nil; // break the retain cycle
        return;
    }
    int i = index;
    next(++i);
};
next(0);

(Примечание: вы все равно получите такое же предупреждение, но, возможно, оно необоснованное)

Вопрос 2: Какова была бы лучшая реализация рекурсии блоков?

Спасибо.

4b9b3361

Ответ 1

Чтобы выполнить выполнение рекурсивного блока без сохранения цикла, вам нужно использовать две ссылки блоков - одну слабую и сильную. Итак, для вашего случая это код выглядит так:

__block __weak void (^weak_next)(int);
void (^next)(int);
weak_next = next = ^(int index) {
  if (index == 3) {
    return;
  }
  int i = index;
  weak_next(++i);
};
next(0);

Обратите внимание, что блок фиксирует ссылку на слабый блок (weak_next), а внешний контекст фиксирует сильную ссылку (далее), чтобы сохранить блок вокруг. Обе ссылки указывают на один и тот же блок.

См. fooobar.com/questions/327664/... для другого примера этого шаблона, который также использует рекурсию блока. Кроме того, здесь обсуждается обсуждение в разделе комментариев следующей статьи: http://ddeville.me/2011/10/recursive-blocks-objc/

Ответ 2

Я думаю, @newacct верен в решении @Matt Wilding; кажется, что в этом случае ничто не будет иметь сильного обращения к следующему блоку и приведет к возникновению исключения времени выполнения при запуске (по крайней мере, для меня).

Я не знаю, насколько распространено найти рекурсивно называемые блоки в дикой природе в objc. Однако в реальном мире (если это действительно необходимо), например, контроллере представления, можно определить блок, а затем настроить свойство внутреннего интерфейса с сильной ссылкой на указанный блок:

typedef void(^PushButtonBlock)();

@interface ViewController ()
@property (strong, nonatomic) PushButtonBlock pushButton;
@end

@implementation ViewController
  ... 
  // (in viewDidLoad or some such)
  __weak ViewController *weakSelf = self;

  self.pushButton = ^() {
    [weakSelf.button pushIt];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), weakSelf.pushButton);
  };

  self.pushButton();
  ...
@end

Это отлично работает для меня и не имеет предупреждений о компиляторе о циклах сохранения (и никаких утечек в инструментах). Но, я думаю, я бы, вероятно, избегал этого (рекурсивных вызовов блоков) в большинстве случаев в objc - это вонючий. Но интересно в любом случае.