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
}