Производительность прокрутки и рисование UIImage - PullRequest
4 голосов
/ 11 октября 2010

Я создаю UITableView, аналогичный представлению просмотра альбомов iPod.app:

IMG_2316.PNG

При первом запуске я импортирую всех исполнителей и обложки альбомов из библиотеки iPod.,Сохранение всего в CoreData и возврат его обратно в NSFetchedResultsController.Я повторно использую идентификаторы ячеек, и в моем методе cellForRowAtIndexPath: у меня есть этот код:

Artist *artist = [fetchedResultsController objectAtIndexPath:indexPath];
NSString *identifier = @"bigCell";

SWArtistViewCell *cell = (SWArtistViewCell*)[tableView dequeueReusableCellWithIdentifier:identifier];

if (cell == nil)
    cell = [[[SWArtistViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];

cell.artistName = artist.artist_name;
cell.artworkImage = [UIImage imageWithData:artist.image];

[cell setNeedsDisplay];

return cell;

В моей ячейке SWArtistViewCell реализован метод drawRect: для рисования как строки, так и изображения:

[artworkImage drawInRect:CGRectMake(0,1,44,44)]
[artistName drawAtPoint:CGPointMake(54, 13) forWidth:200 withFont:[UIFont boldSystemFontOfSize:20] lineBreakMode:UILineBreakModeClip];

Прокрутка все еще прерывистая, и я просто не могу понять, почему.Приложения, такие как iPod и Twitter, имеют плавную прокрутку, и все же они оба рисуют небольшое изображение в ячейке, как и я.

Все мои взгляды непрозрачны.Чего мне не хватает?

РЕДАКТИРОВАТЬ : вот что говорит Акула:

alt text

Я не знаком с Акулой.Любой указатель, с чем связаны эти символы?Когда я смотрю на их след, все они указывают на мой метод drawRect:, в частности, чертеж UIImage.

alt text

Указало бы это на что-то еще, если бы удушение было при чтении файла?Это определенно рисунок?

РЕДАКТИРОВАТЬ: сохранение изображения

Я сделал так, как предложил pothibo, и добавил в свой класс Artist метод artworkImage, который сохраняет изображение, созданное с помощью imageWithData:

- (UIImage*)artworkImage {
    if(artworkImage == nil)
        artworkImage = [[UIImage imageWithData:self.image] retain];

    return artworkImage;
}

Так что теперь я могу напрямую установить сохраненное изображение для моего TableViewCell следующим образом:

cell.artworkImage = artist.artworkImage;

Я также установил свой setNeedsDisplay внутри setArtworkImage: метод моего класса tableViewCell.Прокрутка все еще запаздывает, и Акула показывает точно такие же результаты.

Ответы [ 5 ]

6 голосов
/ 08 февраля 2011

Ваши данные профилирования убедительно свидетельствуют о том, что узким местом является распаковка ваших изображений PNG. Я предполагаю, что 58,5% вашего представленного процессорного времени тратится на распаковку данных PNG (то есть, если вызов memcpy также включен в загрузку). Вероятно, там проводится еще больше времени, но трудно сказать, не имея больше данных. Мои предложения:

  1. Как указано выше, сохраняйте загруженные изображения в UIImage, а не в NSData. Таким образом, вам не придется распаковывать PNG каждый раз при отображении изображения.
  2. Поместите загрузку ваших изображений в рабочий поток, чтобы не влиять на отзывчивость основного потока (как сильно). Создать работника очень просто:

    [self executeSelectorOnMainThread: @selector (preloadThreadEntry :) withObject: nil waitUntilDone: NO];

  3. Предварительная загрузка изображений далеко вперед, например 100 строк или более (например, 70 в направлении прокрутки, удерживайте 30 в противоположном направлении). Если все ваши изображения должны иметь размер 88x88 пикселей на сетчатке, для 100 изображений потребуется не более двух МБ.

  4. Когда вы профилируете больше вызовов для таких вещей, как «png», «gz», «inflate» и т. Д., Могут не попасть в ваш список, но они, безусловно, не окажут такого плохого влияния на ощущение приложения. .

Только если после этого у вас по-прежнему будут проблемы с производительностью, я бы порекомендовал вам изучить масштабирование и, например, загрузку изображений "... @ 2x.png" для сетчатки. Удачи!

4 голосов
/ 11 октября 2010

[UIImage imageWithData:] не кэшируется.

Это означает, что CoreGraphic распаковывает и обрабатывает ваше изображение каждый раз, когда вы передаете этот метод dataSource.

Я бы изменил объект вашего художника, чтобы он удерживал UIImage вместо NSData.Вы всегда можете сбросить изображение в memoryWarning, если вы получаете их много.

Кроме того, я бы не рекомендовал использовать setNeedsDisplay внутри вызова dataSource, я бы использовал это из вашей ячейки.

SetNeedsDisplay не является прямым вызовом drawRect:

Он только сообщает операционной системе рисовать UIVIew снова в конце цикла выполнения.Вы можете вызывать setNeedsDisplay 100 раз за один и тот же цикл запуска, и ОС будет вызывать ваш метод drawRect только один раз.

1 голос
/ 11 октября 2010

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

Лучшим решением было бы хранить изображения в виде отдельных файлов на диске, используя идентификатор альбома для идентификации каждого изображения. Затем вы должны настроить кэш в памяти для хранения изображений в оперативной памяти для быстрой загрузки в ваши UIImageView s. Загрузка изображений с диска в ОЗУ в идеале должна выполняться в фоновом потоке (например, попробуйте выполнить executeSelectorOnBackgroundThread), чтобы ввод-вывод не перегружал основной поток (что повлияет на производительность прокрутки). *

Приложение 1

Если вы звоните -drawRect: только один раз для загрузки ячейки (как и должно быть), то проблема может заключаться в масштабировании изображений. Масштабирование изображения путем рисования его в коде с использованием drawInRect будет использовать процессорное время, поэтому альтернативный подход заключается в масштабировании изображений по мере их получения из библиотеки iPod (если они вам нужны в нескольких размерах, сохраните версию в каждом размере, который вы требуется). Вам может потребоваться сделать это в фоновом потоке при импорте данных, чтобы избежать блокировки основного (UI) потока.

Еще одна альтернатива, которую следует учитывать, - это то, что UIImageView может выполнять масштабирование с использованием Core Animation, что означало бы аппаратное ускорение (я не уверен, так ли это на самом деле, я просто предполагаю). Следовательно, переключение на UIImageView для изображения избавит процессор от нагрузки по масштабированию изображения. Вы можете немного увеличить накладные расходы на компоновку, но это может быть самый простой способ приблизиться к «оптимальной» производительности прокрутки.

1 голос
/ 11 октября 2010

Если задержка происходит в вашем -drawRect, то вы можете взглянуть на эту статью : разработчик Tweetie довольно подробно объясняет метод, который он использовал, чтобы получить ту плавную прокрутку, которую вы ищете. С тех пор это стало немного проще: CALayer обладает свойством shouldRasterize, которое в основном сводит его подслои в растровое изображение, которое может - при условии, что внутри слоя ничего не меняется - дать вам гораздо лучшую производительность при анимации слой вокруг, как UITableView, когда вы прокручиваете его. В этом случае вы, вероятно, примените это свойство к отдельным слоям UITableViewCell s.

0 голосов
/ 11 октября 2010

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

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