GCD - основной и фоновый поток для обновления UIImageView

Я новичок в GCD и блокирую, и я прокладываю себе путь.

Фон: Я работаю над ленивой программой загрузки для UIScrollView, используя ALAssetsLibrary. Когда мои загрузки UIScrollView заполняют его с помощью aspectRatioThumbnails моих ALAssets, а затем, когда пользователь прокручивает, я вызываю подпрограмму ниже, чтобы загрузить fullScreenImage ALAsset, который в настоящее время отображается. Кажется, что это работает.

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

Мой вопрос: Я использую фоновый поток для обработки загрузки fullScreenImage, и когда это будет сделано, я использую основной поток, чтобы применить его к UIImageView. Нужно ли использовать основной поток? Я видел, что все обновления UIKit должны происходить в основном потоке, но я не уверен, что это относится к UIImageView. Я думал, что это так, потому что это элемент экрана, но потом я понял, что просто не знаю.

- (void)loadFullSizeImageByIndex:(int)index
{
    int arrayIndex = index;
    int tagNumber = index+1;
    ALAsset *asset = [self.assetsArray objectAtIndex:arrayIndex];

    __weak typeof(self) weakSelf = self;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage];

        if ([weakSelf.scrollView viewWithTag:tagNumber] != nil){

            dispatch_async(dispatch_get_main_queue(), ^{

                if ([weakSelf.scrollView viewWithTag:tagNumber]!= nil){
                    UIImageView * tmpImageView = (UIImageView*)[weakSelf.scrollView viewWithTag:tagNumber];
                    tmpImageView.image = tmpImage;
                }
            });
        }
    });
}

Ответы

Ответ 1

Да, вам нужно использовать основной поток всякий раз, когда вы касаетесь UIImageView или любого другого класса UIKit (если не указано иное, например, при построении UIImage в фоновом потоке).

Один комментарий к вашему текущему коду: вам нужно назначить weakSelf в сильную локальную переменную перед ее использованием. В противном случае ваше условие могло бы пройти, но тогда weakSelf можно было бы отключить, прежде чем вы попытаетесь его использовать. Это выглядело бы как

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage];

    __strong __typeof__(weakSelf) strongSelf = weakSelf;
    if ([strongSelf.scrollView viewWithTag:tagNumber] != nil){

        dispatch_async(dispatch_get_main_queue(), ^{
            __strong __typeof__(weakSelf) strongSelf = weakSelf;
            if ([strongSelf.scrollView viewWithTag:tagNumber]!= nil){
                UIImageView * tmpImageView = (UIImageView*)[strongSelf.scrollView viewWithTag:tagNumber];
                tmpImageView.image = tmpImage;
            }
        });
    }
});

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

Ответ 2

Если вам нужно сделать изображение в UIImageView, вам нужно сделать это в основном потоке. Он не будет работать, если вы не сделаете это в главной очереди, как показано в вашем коде. То же самое происходит с любым отображением пользовательского интерфейса.

if ([weakSelf.scrollView viewWithTag:tagNumber]!= nil){
    UIImageView * tmpImageView = (UIImageView*)[weakSelf.scrollView viewWithTag:tagNumber];
    tmpImageView.image = tmpImage;
}

По Документация Apple,

Вопросы Threading: Манипуляции к вашим приложениям пользовательский интерфейс должен встречаться в основном потоке. Таким образом, вы всегда должны звонить методы класса UIView из кода, запущенного в основном потоке вашей заявки. Единственный раз, когда это может быть не обязательно при создании самого объекта представления, но все другие манипуляции должно происходить в основном потоке.

Ответ 3

Да, вам нужно использовать основной поток, так как любые изменения пользовательского интерфейса должны выполняться в основном потоке.

Что касается использования GCD, он используется, чтобы воспользоваться преимуществами Multi Cores на устройстве. Что касается сильного и слабого самосостояния

strong self: вам может понадобиться сильное "я" , потому что в вашем коде

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
(<your class> *) *strongSelf = weakSelf;
    UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage];

    if ([strongSelf.scrollView viewWithTag:tagNumber] != nil){

        dispatch_async(dispatch_get_main_queue(), ^{

            if ([strongSelf.scrollView viewWithTag:tagNumber]!= nil){
                UIImageView * tmpImageView = (UIImageView*)[strongSelf.scrollView viewWithTag:tagNumber];
                tmpImageView.image = tmpImage;
            }
        });
    }
});

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

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

Ответ 4

Если вы не будете использовать strongSelf в коде, предложенном Кевином Баллардом, это может привести к краху из-за слабого извлечения.

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

strongSelf = weakSelf

   if(strongSelf)
   {
       // do your stuff here 
   }