Каков наилучший подход к асинхронному кэшированию изображений на iPhone?

StackOverflow https://stackoverflow.com/questions/2259484

Вопрос

Я создаю приложение для iPhone, которое будет извлекать данные из веб-API, включая адреса электронной почты.Я хотел бы отобразить изображение, связанное с каждым адресом электронной почты, в ячейках таблицы, поэтому я ищу изображения в Адресной книге и возвращаюсь к значению по умолчанию, если адреса электронной почты нет в книге.Это отлично работает, но у меня есть несколько проблем:

  • Производительность:Как сообщается, рецепты, которые я нашел для поиска записи в адресной книге по адресу электронной почты (или номеру телефона), следующие довольно медленно.Причина этого заключается в том, что необходимо перебирать каждую запись адресной книги, и для каждой записи, содержащей изображение, перебирать все адреса электронной почты, чтобы найти совпадение.Конечно, для большой адресной книги это может занять много времени.

  • Ячейки таблицы:Поэтому я подумал, что соберу все адреса электронной почты, по которым мне нужно найти изображения, и найду их все сразу.Таким образом, я просматриваю книгу только один раз для всех адресов.Но это плохо работает для ячеек таблицы, где каждая ячейка соответствует одному адресу электронной почты.Мне пришлось бы либо собрать все изображения перед отображением каких-либо ячеек (потенциально медленно), либо заставить каждую ячейку просматривать каждое изображение по мере его загрузки (еще медленнее, поскольку мне нужно было бы пролистать книгу, чтобы найти соответствие для каждый адрес электронной почты).

  • Асинхронный Поиск:Итак, тогда я подумал, что буду искать их массово, но асинхронно, используя NSInvocationOperation.Для каждого изображения, найденного в адресной книге, я бы сохранил миниатюру в песочнице приложения.Тогда каждая ячейка могла бы просто ссылаться на этот файл и показывать значение по умолчанию, если он не существует (потому что его нет в книге или он еще не найден).Если изображение позже будет найдено при асинхронном поиске, то в следующий раз, когда потребуется отобразить изображение, оно появится внезапно.Это может хорошо сработать для периодической регенерации изображений (например, когда изображения были изменены в адресной книге).Но тогда для любого данного экземпляра моего приложения изображение может на самом деле некоторое время не отображаться.

  • Асинхронный Поиск по ячейкам Таблицы:В идеале я бы использовал что-то вроде асинхронное обновление ячейки таблицы markjnet для обновления ячеек таблицы изображением после его загрузки.Но для того, чтобы это сработало, мне пришлось бы создать NSInvocationOperation задание для каждой ячейки по мере ее отображения и при отсутствии кэшированного значка в изолированной среде.Но затем мы возвращаемся к неэффективному перебору всей адресной книги для каждого из них — а их может быть много, если вы только что загрузили целую кучу новых адресов электронной почты.

Итак, мой вопрос заключается в следующем:Как другие это делают?Я возился с Tweetie2, и, похоже, он обновляет отображаемые ячейки таблицы асинхронно.Я предполагаю, что он отправляет отдельный HTTP-запрос для каждого необходимого ему изображения.Если это так, я полагаю, что поиск в локальной адресной книге по адресу электронной почты ничуть не менее эффективен, так что, может быть, это лучший подход?Просто не беспокоиться о проблемах с производительностью, связанных с поиском в адресной книге?

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

Как остальные из вас решают такого рода проблемы?Предложения будут высоко оценены!

Это было полезно?

Решение

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

Создайте NSMutableDictionary, который будет служить вашим кешем в памяти для результатов поиска.Инициализируйте этот словарь каждым адресом электронной почты из загрузки в качестве ключа, со значением sentinel в качестве значения этого ключа (например, [NSNull null]).

Затем выполните итерацию по каждому ABRecordRef в Адресной книге, вызывая ABRecordCopyValue(record, kABPersonEmailProperty) и циклический просмотр результатов в каждом возвращаемом ABMultiValue.Если какой-либо из адресов электронной почты является ключом в вашем кэше, установите [NSNumber numberWithInt:ABRecordGetRecordId(record)] как значение этого ключа в вашем словаре.

Используя этот словарь в качестве индекса поиска, вы можете быстро получить изображения ABRecordRefs только для тех адресов электронной почты, которые вы в данный момент отображаете в своем табличном представлении, учитывая текущую позицию прокрутки пользователя, как предложено в ответе hoopjones .Вы можете добавить прослушиватель изменений адресной книги, чтобы сделать ваш кэш недействительным, запустить другую операцию индексации, а затем обновить представление, если вашему приложению требуется такой уровень "актуальности".

Другие советы

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

Что-то вроде этого (это немного изменено из учебника, который я нашел в Интернете, который я сейчас не могу найти, приношу извинения за то, что не цитирую автора) :

- (void)loadContentForVisibleCells
{
    NSArray *cells = [self.table visibleCells];
    [cells retain];
    for (int i = 0; i < [cells count]; i++) 
    { 
        // Go through each cell in the array and call its loadContent method if it responds to it.
        AddressRecordTableCell *addressTableCell = (AddressRecordTableCell *)[[cells objectAtIndex: i] retain];
        [addressTableCell loadImage];
        [addressTableCell release];
        addressTableCell = nil;
    }
    [cells release];
}


- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; 
{
    // Method is called when the decelerating comes to a stop.
    // Pass visible cells to the cell loading function. If possible change 
    // scrollView to a pointer to your table cell to avoid compiler warnings
    [self loadContentForVisibleCells]; 
}


- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
{
    if (!decelerate) 
    {
       [self loadContentForVisibleCells]; 
    }
}

Как только вы узнаете, какие записи адресов видны в данный момент, просто выполните поиск по этим записям (вероятно, 5-7 записей) с молниеносной скоростью.Как только вы получите изображение, просто кешируйте его в словаре, чтобы вам не пришлось повторять запрос на изображение позже.

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

К вашему сведению, я выпустил бесплатную, мощную и простую библиотеку для асинхронной загрузки изображений и быстрого кэширования файлов:Управляемые объекты HJ http://www.markj.net/asynchronous-loading-caching-images-iphone-hjobjman/

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top