#ios #ios5 #catiledlayer
#iOS #ios5 #catiledlayer
Вопрос:
У меня проблема с CATiledLayer. Он отлично работает на iOS 4, но имеет проблемы на iOS 5.
Моя проблема в том, что drawRect
она вызывается дважды для одного и того же rect из двух разных потоков одновременно. Поскольку я загружаю изображения в этом вызове, это приводит к очень медленной загрузке представления.
2011-10-18 14:07:18.802 APP[12436:19003] drawRect:{{0, 400}, {368, 400}} (view:<TiledScrollColumn: 0x91bc880; frame = (0 1600; 368 5400); userInteractionEnabled = NO; layer = <CATiledLayer: 0x919c1b0>>)
2011-10-18 14:07:18.805 APP[12436:1b103] drawRect:{{0, 400}, {368, 400}} (view:<TiledScrollColumn: 0x91bc880; frame = (0 1600; 368 5400); userInteractionEnabled = NO; layer = <CATiledLayer: 0x919c1b0>>)
Кто-нибудь знает, что может вызвать это или что я мог бы сделать, чтобы это исправить? Я не делаю ничего особенного с представлением, оно основано на примере photoscroller.
Бастиан
Ответ №1:
Я нашел обходной путь. Создайте последовательную (только одну операцию за раз) очередь отправки и выполните все ваши чертежи внутри нее, затем кэшируйте результат.
Я прикрепил код, который я использую, обратите внимание, что я использую подкласс CATiledLayer
, вместо того, чтобы рисовать его с помощью делегата или других методов.
Я также создаю кеш с неопределенным тайлом, который может вызвать проблемы с памятью в зависимости от вашей ситуации. Возможно, вам потребуется управлять количеством плиток в кэше, удаляя старые по мере добавления новых. Кэш также следует очищать при получении предупреждения о нехватке памяти. Если кто-нибудь добавит эти улучшения, пожалуйста, не стесняйтесь редактировать мой ответ, чтобы включить его.
- (id)init
{
if (!(self = [super init]))
return nil;
tileCache = [[NSMutableDictionary alloc] init];
return self;
}
- (void)drawInContext:(CGContextRef)layerContext
{
// CATiledLayer has a bug, where it spawns multiple threads for drawing, and then tries to draw the same tile multiple times simultaniously on separate threads
// so we create our own serial background queue, and do dispatch_async on it. This will cache each draw operation, so multiple calls on one tile are efficient
static dispatch_queue_t drawQueue = NULL;
static dispatch_once_t onceToken;
dispatch_once(amp;onceToken, ^{
drawQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
});
dispatch_sync(drawQueue, ^{
// no map ways? draw nothing
if (!self.mapWays)
return;
// load from cache?
CGRect tileRect = CGContextGetClipBoundingBox(layerContext);
NSString *tileCacheKey = [NSString stringWithFormat:@"%f%f%f%f", tileRect.origin.x, tileRect.origin.y, tileRect.size.width, tileRect.size.height];
__block UIImage *tileImage;
dispatch_sync(dispatch_get_main_queue(), ^{
tileImage = [tileCache objectForKey:tileCacheKey];
});
if (tileImage) {
CGContextDrawImage(layerContext, tileRect, tileImage.CGImage);
return;
}
// prepare to draw the tile image
UIGraphicsBeginImageContextWithOptions(tileRect.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// filp coords
CGContextTranslateCTM(context, 0, tileRect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
/*** do actual drawing here ***/
// store tile in cache
tileImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_sync(dispatch_get_main_queue(), ^{
[tileCache setObject:tileImage forKey:tileCacheKey];
});
// draw the tile
CGContextDrawImage(layerContext, tileRect, tileImage.CGImage);
});
}
Комментарии:
1. Я бы подумал, что вы захотите использовать NSCache вместо NSMutableDictionary, чтобы он обрабатывал проблемы с памятью для вас. Кроме того, похоже, что этот подход сводит на нет преимущества CATiledLayer, поскольку вы собираетесь загружать все изображение в память, делая это.