До iOS 3.2 я использовал этот вид кода для загрузки UIImageView
изображение в фоне, и это хорошо работало...
Код:
- (void)decodeImageName:(NSString *)name
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *newImage = [UIImage imageNamed:name];
[myImageView setImage:newImage];
[pool release];
}
...
[self performSelectorInBackground:@selector(decodeImageName:) withObject:@"ID"]
... даже если [UIImageView setImage:]
не было ориентировано на многопотоковое исполнение!
Но начиная с iOS 4, это больше не работает... Изображения появляются на экране спустя две секунды после этого setImage
звонить. И если я делаю a [myImageView performSelectorOnMainThread:@selector(setImage:) withObject:newImage waitUntilDone:YES]
вместо [myImageView setImage:newImage]
, изображения сразу появляются, но, кажется, повторно декодируются снова на лету (игнорирующий предыдущее [UIImage imageNamed:]
который должен был уже декодировать данные изображения), вызывая паузу на моем основном потоке... Даже если в документации говорится, что кэш основного изображения общий для все потоки..
Какая-либо мысль?
Не делайте этого в фоновом режиме! Это не потокобезопасный. Поскольку UIImageView
также является NSObject
, я думаю, что использование - [performSelectorOnMainThread: withObject: waitUntilDone:]
может сработать, например:
[myImageView performSelectorOnMainThread:@selector(setImage:) withObject:newImage waitUntilDone:NO];
И это UIImage
, который недавно стал потокобезопасным. UIImageView
по-прежнему не является потокобезопасным.
performSelectorInBackground:
запускает селектор в фоновом потоке.Но setImage: - это функция пользовательского интерфейса. Функции пользовательского интерфейса должны выполняться только в основном потоке. У меня нет понимания конкретной проблемы, но это первое впечатление об этом коде, и возможно, что iOS4 каким-то образом по-другому обрабатывает (не поддерживаемый) механизм запуска функций пользовательского интерфейса в фоновых потоках.
Если вы используете iOS 4.0, вам следует действительно рассмотреть возможность чтения блоков и GCD. Используя эти технологии, вы можете просто заменить свой метод на:
- (void)decodeImageName:(NSString *)name
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *newImage = [UIImage imageNamed:name];
dispatch_async(dispatch_get_main_queue(), ^{
[myImageView setImage:newImage];
}
[pool release];
}
Процитируем:
@property (неатомарно, только для чтения) CGImageRef CGImage
Обсуждение
Если данные изображения были очищены из-за ограничений памяти, вызов этого метода заставляет эти данные для загрузки обратно в память. Повторная загрузка данных изображения может привести к снижению производительности.
Так что вы могли бы просто вызвать image.CGImage
. Не думаю, что CGImages
ленивы.
Если это не сработает, вы можете принудительно выполнить рендеринг с помощью чего-то вроде
// Possibly only safe in the main thread...
UIGraphicsBeginImageContext((CGSize){1,1});
[image drawInRect:(CGRect){1,1}];
UIGraphicsEndImageContext();
Некоторые люди предупреждают о потокобезопасности. В документации говорится, что UIGraphics {Push, Pop, GetCurrent} Context ()
работает только с основным потоком, но ничего не упоминается о UIGraphicsBeginImageContext ()
. Если вы беспокоитесь, используйте CGBitmapContextCreate
и CGContextDrawImage
.