Каков наилучший подход к асинхронному кэшированию изображений на iPhone? - PullRequest
10 голосов
/ 14 февраля 2010

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

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

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

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

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

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

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

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

Ответы [ 4 ]

2 голосов
/ 15 февраля 2010

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

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

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

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

2 голосов
/ 14 февраля 2010

Я бы использовал последний метод, который вы перечислили (Асинхронный поиск ячеек таблицы), но просматривал только изображения для текущих отображаемых записей. Я перегружаю методы 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 записей) будет молниеносно. Как только вы захватите изображение, просто кешируйте его в словаре, чтобы вам не приходилось повторять запрос на изображение позже.

1 голос
/ 14 февраля 2010

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

0 голосов
/ 13 января 2011

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

...