Подтвердить что ты не робот

Действительно ли слабый Self/strongSelf танец необходим, когда вы ссылаетесь на себя внутри незакрепленного завершения, вызванного из UIViewController?

Скажем, у меня есть следующий метод внутри подкласса UIViewController:

- (void)makeAsyncNetworkCall
{
    [self.networkService performAsyncNetworkCallWithCompletion:^{
        dispatch_async(dispatch_get_main_queue(), ^{
                [self.activityIndicatorView stopAnimating];
            }
        });
    }];
}

Я знаю, что ссылка на self внутри блока приводит к тому, что экземпляр UIViewController сохраняется в блоке. Пока performAsyncNetworkCallWithCompletion не сохраняет блок в свойстве (или ivar) на моем NetworkService, правильно ли я думаю, что цикл сохранения отсутствует?

Я понимаю, что эта структура выше приведет к тому, что UIViewController будет сохранен до завершения performAsyncNetworkCallWithCompletion, даже если он будет выпущен системой ранее. Но возможно ли это (или даже возможно?) Система освободит мой UIViewController вообще ( после изменений в способе iOS 6 управляет UIViewController поддержкой CALayer памяти)?

Если есть причина, я должен сделать "слабый самец/сильный танец", это будет выглядеть так:

- (void)makeAsyncNetworkCall
{
    __weak typeof(self) weakSelf = self;
    [self.networkService performAsyncNetworkCallWithCompletion:^{
        typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return;
        }
        dispatch_async(dispatch_get_main_queue(), ^{
                [strongSelf.activityIndicatorView stopAnimating];
            }
        });
    }];
}

Но я нахожу это неразумно уродливым и хотел бы избежать этого, если это не нужно.

4b9b3361

Ответ 1

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

Нет необходимости в конструкции strongSelf. Вы можете:

- (void)makeAsyncNetworkCall
{
    __weak typeof(self) weakSelf = self;
    [self.networkService performAsyncNetworkCallWithCompletion:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf.activityIndicatorView stopAnimating];
        });
    }];
}

Вам нужна только комбинация weakSelf/strongSelf, где важно иметь сильную ссылку (например, вы разыгрываете ивары), или если вам нужно беспокоиться о состоянии гонки. Это, похоже, не так.

Ответ 2

Я думаю, что проблема в том, что networkService может содержать сильную ссылку на блок. И контроллер вида может иметь сильную ссылку на networkService. Таким образом, может существовать возможный цикл VC- > NetworkService- > block- > VC. Однако в этом случае обычно безопасно предполагать, что блок будет выпущен после его запуска, и в этом случае цикл будет нарушен. Таким образом, в этом случае это необязательно.

Где это необходимо, если блок не освобожден. Скажем, вместо того, чтобы иметь блок, который запускается один раз после сетевого вызова, у вас есть блок, который используется как обратный вызов. т.е. объект networkService поддерживает сильную ссылку на блок и использует его для всех обратных вызовов. В этом случае блок будет иметь сильную ссылку на VC, и это создаст сильный цикл, поэтому предпочтительной является слабая ссылка.

Ответ 3

Нет, если ваш self.networkService не использует его как свойство блока, вы должны быть в порядке

Ответ 4

Ответ здесь не так прост. Я согласен с ответом @Rob, но ему нужно дополнительное объяснение:

  • __weak считается безопасным способом, поскольку он освобождает себя при выпуске, что означает, что не будет никакого исключения, если обратный вызов произойдет намного позже, когда вызывающий объект уже выпущен, на который ссылается блок, например UIViewController выскочил из стека. Добавление возможности отмены какой-либо операции - это просто вопрос гигиены и, возможно, ресурсов. Вы можете, например, также просто отменить NSURLConnection, это может быть отменено не только NSOperation, вы можете отменить все, что выполняется асинхронно, в методе, который обращается к блоку.

  • Если self будет сохранено блоком, история может немного усложниться, если объект-вызывающий объект, такой как UIViewController, освобождается UINavigationController, и блок все еще сохраняет его и перезванивает. В этом случае блок обратного вызова будет выполнен и предполагается, что некоторые данные будут изменены по его результатам. Это может быть даже желание поведения, но в большинстве случаев нет. Поэтому отмена операции может быть более существенной в этом случае, что делает ее очень мудрой в методах UINavigationControllerDelegate, отменяя асинхронные задачи из изменяемой коллекции, которые находятся либо на UINavigationController как на ассоциированный объект, либо как одиночный.

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