Как исправить "Захват" блока "сильно в этом блоке, вероятно, приведет к циклу сохранения"

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

-(void) performOperation
{

    void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request){


        int variable=0;

        // Do completion operation A
        //...
        //...

        // Do completion operation B                
        //Get the variable value

        if(variable>0){
            [self doLengthyAsynchronousOperationWithCompletionBlock: completionBlock];
        }

    };

//Perform the lenhgty operation with the above completionBlock
    [self doLengthyAsynchronousOperationWithCompletionBlock: completionBlock];

}

-(void) doLengthyAsynchronousOperationWithCompletionBlock: completionBlock
{
    //Do some lengthy asynchronous stuff
}

С помощью этого кода я получаю это предупреждение от компилятора:

WARNING: Block pointer variable 'completionBlock' is uninitialized when caputerd by the block

Я изменил:

void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request)

в

__block void(^completionBlock) (id obj, NSError *err, NSURLRequest *request)= ^(id obj,NSError *err, NSURLRequest *request)

но я получаю это другое предупреждение:

WARNING 2: Capturing 'completionBlock' strongly in this block is likely to lead to a retain cycle

Как я могу это исправить?

Спасибо

Nicola

Ответ 1

ПРЕДУПРЕЖДЕНИЕ: переменная указателя блока "completeBlock" не инициализирована при захвате блоком

Это происходит потому, что блок-переменные, инициализированные для рекурсивного блока, нуждаются в хранилище __block.

  • Переменные внутри блока копируются, если не объявлены с помощью __block, и в этом случае они передаются в качестве ссылки.
  • Когда рекурсивный блок присваивается блочной переменной, создание происходит до назначения, и такое создание запускает переменную копию. Учитывая, что переменная еще не назначена, скопированная переменная будет плохим значением и приведет к сбою при запуске блока.
  • Но если мы добавим __block, блок будет создан вместо ссылки на переменную. Затем переменная будет инициализирована в созданном блоке, и блок будет готов к использованию.

ПРЕДУПРЕЖДЕНИЕ: Захват "завершения" блокировки в этом блоке вероятен чтобы привести к циклу удержания

Это происходит потому, что блочная переменная является сильной ссылкой на блок, и блок сам ссылается на переменную (поскольку, как мы видели ранее, переменная имеет __block, поэтому вместо нее она ссылается).

Итак, нам нужно

  • Слабая ссылка на сильную переменную внутри блока.
  • И сильная ссылка снаружи, чтобы предотвратить освобождение блока во время действия метода, в котором он создан.
    void(^ completionBlock) (id obj, NSError *err, NSURLRequest *request);
    void(^ __block __weak weakCompletionBlock) (id obj, NSError *err, NSURLRequest *request);
    weakCompletionBlock = completionBlock = ^(id obj,NSError *err, NSURLRequest *request){
        [self lengthyAsyncMethod:weakCompletionBlock];
    };

Имя doLengthyAsynchronousOperationWithCompletionBlock предполагает, что метод может пережить область метода, в которой создается блок. Учитывая, что компилятор не копирует блок, переданный в качестве аргумента, ответственность этого метода заключается в том, чтобы скопировать этот блок. Если мы используем этот блок с блочным кодом (например: dispatch_async()), это происходит автоматически.

Если бы мы назначили этот блок переменной экземпляра, нам понадобится @property(copy) и слабая ссылка на self внутри блока, но это не так, поэтому мы просто используем self.