Объясните причины использования __weak и __strong в коде SDWebImage.

Думаю, я хорошо разбираюсь в сильных и слабых ключевых словах, но не понимаю, как они используются в приведенном ниже коде. Этот код взят из SDWebImage от Olivier Poitrey и доступен на github. Я понимаю сильные и слабые ключевые слова, как описано здесь: Объяснение сильных и слабых хранилище в iOS5

В приведенном ниже коде ключевые слова __weak и __strong используются любопытным для меня образом. Это не отношения ребенок-родитель или образец делегирования, поскольку я привык видеть слабое использование. Однако я уверен, что этот шаблон используется часто, поскольку я видел его раньше в другом коде. Он устанавливает ссылку __weak перед блоком, который выполняется в другом потоке. Затем внутри блока слабая ссылка заменяется сильной ссылкой.

Я уверен, что это хороший и элегантный код, поэтому я пытаюсь его понять. Если «я» перестанет существовать до того, как блок запустится, слабая ссылка на себя будет обнулена. Когда блок запускается, сильная ссылка также будет установлена ​​на ноль. Следовательно, он будет знать, что нужно завершить остальную часть операции, поскольку self больше не существует. Я правильно понял?

Что произойдет, если мы не будем использовать ключевые слова __weak и __strong? Что, если мы просто проверим внутри блока, является ли self == nil. Будет ли «я» никогда не равным нулю, поскольку блок копирует все дерево?

Может ли кто-нибудь помочь демистифицировать этот удивительный фрагмент кода? Может ли кто-нибудь подтвердить или опровергнуть мои предположения?

- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock;
{
    [self cancelCurrentImageLoad];

    self.image = placeholder;

    if (url)
    {
        __weak UIImageView *wself = self;
        id<SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished)
        {
            __strong UIImageView *sself = wself;
            if (!sself) return;
            if (image)
            {
                sself.image = image;
                [sself setNeedsLayout];
            }
            if (completedBlock && finished)
            {
                completedBlock(image, error, cacheType);
            }
        }];
        objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
}

person Ed Chin    schedule 16.02.2013    source источник


Ответы (2)


Метод downloadWithUrl: может занять много времени. За это время пользователь может решить уйти, избавляясь от необходимости в объекте SDWebImage. Чтобы облегчить раннюю очистку объекта, внешняя ссылка self является слабой. Таким образом, downloadWithUrl не предотвратит освобождение SDWebImage.

Конечно, если вы действительно хотите работать с self, вам нужна надежная рекомендация. Таким образом, завершающий блок downloadWithUrl получает сильную ссылку на self. Если объект исчезнет за это время, sself будет nil. В противном случае это будет действительная сильная ссылка, указывающая, что объект SDWebImage все еще существует, и в это время объект завершит свою работу.

person nneonneo    schedule 16.02.2013
comment
Еще один фантастический ответ. Спасибо, неоннео. - person Ed Chin; 16.02.2013
comment
Это больше, чем просто облегчить раннюю очистку объекта. Это вообще облегчает очистку объекта — иначе был бы цикл сохранения — self сохраняет operation через ассоциативную ссылку. operation сохраняет блок. Если бы блок сохранил self, это был бы цикл. - person newacct; 16.12.2013

Я уверен, что это хороший и элегантный код, поэтому я пытаюсь его понять. Если «я» перестанет существовать до того, как блок запустится, слабая ссылка на себя будет обнулена. Когда блок запускается, сильная ссылка также будет установлена ​​на ноль. Следовательно, он будет знать, что нужно завершить остальную часть операции, поскольку self больше не существует. Я правильно понял?

Нет, ты слишком много думаешь об этом. Квалификатор хранилища __weak — это именно квалификатор. Объекты со значением __weak явно не сохраняются, но они не просто автоматически устанавливаются равными nil, если они назначаются из сильной переменной. На самом деле, это противоречит цели слабой переменной!

Что произойдет, если мы не будем использовать ключевые слова __weak и __strong? Что, если мы просто проверим внутри блока, является ли self == nil. Будет ли «я» никогда не равным нулю, поскольку блок копирует все дерево?

Проверка на самом деле не нужна, потому что среда выполнения разрешает сообщения от nil к nil (тем не менее, это может быть важно для реализации позже, кто знает). Вы попали в точку с этим: без этого маленького танца «от слабого к сильному» я был бы удержан блоком, что может создать довольно неприятный цикл удержания. Вот где я могу начать связывать все это вместе:

Поскольку мы не хотим, чтобы блок сохранял нашу переменную, но мы также хотим, чтобы она была строгой внутри области действия блока, чтобы ничего странного не происходило, self присваивается слабому указателю. Когда блок происходит по нашему слабому указателю, его нельзя сохранить, поэтому счетчик ссылок self остается прежним, затем, оказавшись внутри блока, мы возвращаемся обратно к сильной переменной self, поэтому слабый освобождается, и мы не нужно больше беспокоиться об этом. На практике это означает, что у нас есть твердая гарантия того, что self является либо значением, либо нулем на протяжении всего выполнения блока. Довольно аккуратно, да?

person CodaFi    schedule 16.02.2013
comment
Ваш последний абзац очень полезен. Спасибо КодаФай. - person Ed Chin; 16.02.2013
comment
Я не понимаю эту фразу: ... мы возвращаемся к сильной собственной переменной, поэтому слабая освобождается. Существует ли sself для создания сильной ссылки, чтобы убедиться, что мы не будем освобождены в середине выполнения блока другим потоком? - person Boon; 16.12.2013
comment
Вроде, как бы, что-то вроде. Если вы не переназначите сильную переменную в блоке, она может быть освобождена из-под нас и установлена ​​в nil в середине выполнения блока. Назначая strong, вы гарантируете, что self будет либо ненулевым, либо нулевым на протяжении всей продолжительности выполнения блока. - person CodaFi; 16.12.2013