Я пытаюсь загрузить UIImages в фоновом потоке, а затем отобразить их на iPad. Однако, когда я устанавливаю свойство view imageViews для изображения, возникает заикание. Вскоре я понял, что загрузка изображений на iOS является ленивой, и нашел частичное решение в этом вопросе:
Ленивая загрузка CGImage / UIImage в потоке пользовательского интерфейса вызывает заикание
На самом деле это заставляет изображение загружаться в поток, но при отображении изображения все равно происходит заикание.
Вы можете найти мой образец проекта здесь: http://www.jasamer.com/files/SwapTest.zip (отредактируйте: фиксированная версия), проверьте SwapTestViewController. Попробуйте перетащить картинку, чтобы увидеть заикание.
Я создал тестовый код, который заикается (метод forceLoad взят из вопроса о переполнении стека, который я опубликовал выше):
NSArray* imagePaths = [NSArray arrayWithObjects:
[[NSBundle mainBundle] pathForResource: @"a.png" ofType: nil],
[[NSBundle mainBundle] pathForResource: @"b.png" ofType: nil], nil];
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock: ^(void) {
int imageIndex = 0;
while (true) {
UIImage* image = [[UIImage alloc] initWithContentsOfFile: [imagePaths objectAtIndex: imageIndex]];
imageIndex = (imageIndex+1)%2;
[image forceLoad];
//What's missing here?
[self performSelectorOnMainThread: @selector(setImage:) withObject: image waitUntilDone: YES];
[image release];
}
}];
Я знаю, что заикания можно избежать по двум причинам:
(1) Apple может загружать изображения без задержек в приложении «Фото».
(2) Этот код не вызывает заикания после того, как placeholder1 и placeholder2 были отображены один раз в этой модифицированной версии приведенного выше кода:
UIImage* placeholder1 = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource: @"a.png" ofType: nil]];
[placeholder1 forceLoad];
UIImage* placeholder2 = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource: @"b.png" ofType: nil]];
[placeholder2 forceLoad];
NSArray* imagePaths = [NSArray arrayWithObjects:
[[NSBundle mainBundle] pathForResource: @"a.png" ofType: nil],
[[NSBundle mainBundle] pathForResource: @"b.png" ofType: nil], nil];
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock: ^(void) {
int imageIndex = 0;
while (true) {
//The image is not actually used here - just to prove that the background thread isn't causing the stutter
UIImage* image = [[UIImage alloc] initWithContentsOfFile: [imagePaths objectAtIndex: imageIndex]];
imageIndex = (imageIndex+1)%2;
[image forceLoad];
if (self.imageView.image==placeholder1) {
[self performSelectorOnMainThread: @selector(setImage:) withObject: placeholder2 waitUntilDone: YES];
} else {
[self performSelectorOnMainThread: @selector(setImage:) withObject: placeholder1 waitUntilDone: YES];
}
[image release];
}
}];
Однако я не могу сохранить в памяти все свои изображения.
Это означает, что forceLoad не выполняет всю работу - до фактического отображения изображений происходит что-то еще. Кто-нибудь знает, что это такое, и как я могу поместить это в фоновый поток?
Спасибо, Джулиан
Обновить
Использовал несколько советов Томми. Я понял, что именно CGSConvertBGRA8888toRGBA8888 занимает так много времени, поэтому кажется, что это преобразование цвета вызывает задержку. Вот (перевернутый) стек вызовов этого метода.
Running Symbol Name
6609.0ms CGSConvertBGRA8888toRGBA8888
6609.0ms ripl_Mark
6609.0ms ripl_BltImage
6609.0ms RIPLayerBltImage
6609.0ms ripc_RenderImage
6609.0ms ripc_DrawImage
6609.0ms CGContextDelegateDrawImage
6609.0ms CGContextDrawImage
6609.0ms CA::Render::create_image_by_rendering(CGImage*, CGColorSpace*, bool)
6609.0ms CA::Render::create_image(CGImage*, CGColorSpace*, bool)
6609.0ms CA::Render::copy_image(CGImage*, CGColorSpace*, bool)
6609.0ms CA::Render::prepare_image(CGImage*, CGColorSpace*, bool)
6609.0ms CALayerPrepareCommit_(CALayer*, CA::Transaction*)
6609.0ms CALayerPrepareCommit_(CALayer*, CA::Transaction*)
6609.0ms CALayerPrepareCommit_(CALayer*, CA::Transaction*)
6609.0ms CALayerPrepareCommit_(CALayer*, CA::Transaction*)
6609.0ms CALayerPrepareCommit
6609.0ms CA::Context::commit_transaction(CA::Transaction*)
6609.0ms CA::Transaction::commit()
6609.0ms CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*)
6609.0ms __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
6609.0ms __CFRunLoopDoObservers
6609.0ms __CFRunLoopRun
6609.0ms CFRunLoopRunSpecific
6609.0ms CFRunLoopRunInMode
6609.0ms GSEventRunModal
6609.0ms GSEventRun
6609.0ms -[UIApplication _run]
6609.0ms UIApplicationMain
6609.0ms main
К сожалению, последние предложенные им изменения битовой маски ничего не изменили.
NSBundle
не является потокобезопасным. - person SK9   schedule 11.06.2012