Операция fetchedResultsController.object (at: indexPath), блокирующая основной поток - PullRequest
0 голосов
/ 09 июня 2018

Я создаю приложение, которое отображает картинки в виде коллекции.Я загружаю JSON из общедоступного API GIPHY, затем получаю данные из каждого отдельного URL-адреса GIF и использую FLAnimatedImage для сохранения данных в UIImageView перед их отображением в представлении коллекции.

Iразветвил FLAnimatedImage pod и добавил NSCoding протоколы, чтобы данные можно было архивировать в Core Data.

В моем collectionView я использую fetchedResultsController для извлечения объектов, которые заполняютсяmy collectionView.

Из-за подкачки мой API запрашивает новые изображения каждый раз, когда я получаю полпути вниз по изображениям в моем collectionView (один столбец, одна строка) в cellForItemAt.

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

Теперь вот проблема: я сохраняю каждый GIF, как только получаю данные из задачи загрузки в виде FLAnimatedImage в моем стеке данных ядра.Теперь его разархивирование приводит к повторному запуску создания FLAnimatedImage для каждого изображения, что приводит к значительной загрузке ЦП, поскольку проблема в создании FLAnimatedImage очень загружает ЦП и поэтому не архивирует его из стека.Таким образом, обе эти операции выполняются для новых пакетов изображений во время подкачки, и в итоге мое приложение вылетает.

Замедление происходит при каждом вызове fetchedResultsController.object(at: indexPath), и я использую backgroundContext, поэтому я знаю, что это не потокЯ на.Я думаю, что это просто общая загрузка процессора, которая вызывает сбой.Но разве fetchedResultsController не должен был вытянуть сущность из стека?Почему он разархивирует его и создает тип атрибута (FLAnimatedImage) в вызове fetchedResultsController.object(at: indexPath)?

Есть ли способ сохранить FLAnimatedImage в стеке, который не потребует его повторного создания каждый разЯ вытаскиваю его из стека с помощью fetchedResultsController.object(at: indexPath)?

Можете ли вы придумать стратегию, которая может помочь?

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GSGifCollectionViewCell", for: indexPath) as! GSGifCollectionViewCell

    let gifObject:NSManagedObject = self.fetchedResultsController.object(at: indexPath)

    gifObject.managedObjectContext?.perform {
        let gsGifObject = gifObject as? GSGif
        DispatchQueue.main.async {
            cell.imageView.animatedImage = gsGifObject?.image
            cell.rankLabel.text = "\(gsGifObject?.rank  ?? 0)"
        }
    }


    if let totalObjectsForSectionZero = fetchedResultsController.sections?[0].numberOfObjects {
        self.checkCurrentIndexPathAndGetMoreGifsIfNeccessary(indexPath: indexPath, totalObjects:totalObjectsForSectionZero)
    }


    return cell
}

Есть ли что-то, что я делаю неправильно?

Вот мой архивный код:

#import "FLAnimatedImage+NSCoding.h"

@implementation FLAnimatedImage (NSCoding)

static NSString *const kFLAnimatedImageCodingData = @"kFLAnimatedImageCodingData";

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    NSData *data = [aDecoder decodeObjectForKey:kFLAnimatedImageCodingData];
    return [self initWithAnimatedGIFData:data];
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:self.data forKey:kFLAnimatedImageCodingData];
}

@end

Это приводит к блокировке основного потока - [self initWithAnimatedGIFData: data];

1 Ответ

0 голосов
/ 10 июня 2018

Первое - данные ядра не являются потокобезопасными - ни для чтения, ни для записи.Поэтому замените

 gifObject.managedObjectContext?.perform {
    let gsGifObject = gifObject as? GSGif
    DispatchQueue.main.async {
        cell.imageView.animatedImage = gsGifObject?.image
        cell.rankLabel.text = "\(gsGifObject?.rank  ?? 0)"
    }
}

просто:

 let gsGifObject = gifObject as? GSGif
 cell.imageView.animatedImage = gsGifObject?.image
 cell.rankLabel.text = "\(gsGifObject?.rank  ?? 0)"

Переход к фоновому потоку и обратно не помогает и нарушает правила основных данных для многопоточности.

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

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

b) создать другую сущность, которая имеет только большой двоичный объект данных, и установить связь с ним из GSGif.таким образом, связь загружается (в основных данных говорят «ошибочно»), когда к ней обращаются.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...