Desempenho de exibição de rolagem e exibição de tabela ao carregar imagens do disco

Estou tentando melhorar o desempenho do meu aplicativo para iPhone com uso intenso de imagens usando um cache de imagens com base em disco em vez de passar pela rede. Modelei meu cache de imagens após o SDImageCache (http://github.com/rs/SDWebImage/blob/master/SDImageCache.m) e é praticamente o mesmo, mas sem operações de entrada / saída de cache assíncrono.

Tenho algumas visualizações de rolagem e de tabela que carregam essas imagens de forma assíncrona. Se a imagem estiver no disco, ela será carregada do cache da imagem; caso contrário, uma solicitação de rede será feita e o resultado subsequente será armazenado no cache.

O problema que encontro é que, ao percorrer as visualizações de rolagem ou de tabela, há um atraso perceptível à medida que a imagem é carregada do disco. Em particular, a animação de ir de uma página para outra em uma exibição de rolagem tem um pequeno congelamento no meio da transição.

Eu tentei corrigir isso da seguinte maneira:

Usar os objetos NSOperationQueue e NSInvocationOperation para fazer solicitações de acesso ao disco (da mesma maneira que SDImageCache), mas não ajuda em nada com o atraso.Ajustar o código do controlador de exibição de rolagem para que ele carregue imagens apenas quando a exibição de rolagem não estiver mais rolando. Isso significa que o acesso ao disco é acionado apenas quando a exibição de rolagem para de rolar, mas se eu tentar rolar imediatamente para a próxima página, notarei o atraso conforme a imagem é carregada do disco.

Existe uma maneira de melhorar o desempenho dos acessos ao disco ou ter menos efeito na interface do usuário?

Observe que já estou armazenando as imagens na memória também. Assim, quando tudo é carregado na memória, a interface do usuário é agradável e responsiva. Mas quando o aplicativo é iniciado, ou se avisos de pouca memória forem enviados, experimentarei muitos desses atrasos na interface do usuário à medida que as imagens são carregadas do disco.

Os trechos de código relevantes estão abaixo. Eu não acho que estou fazendo algo chique ou louco. O atraso não parece ser perceptível em um iPhone 3G, mas é bastante aparente em um iPod Touch de segunda geração.

Código de armazenamento em cache da imagem:

Aqui está um trecho relevante do meu código de cache de imagem. Bem direto.

- (BOOL)hasImageDataForURL:(NSString *)url {
    return [[NSFileManager defaultManager] fileExistsAtPath:[self cacheFilePathForURL:url]];
}

- (NSData *)imageDataForURL:(NSString *)url {
    NSString *filePath = [self cacheFilePathForURL:url];

    // Set file last modification date to help enforce LRU caching policy
    NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
    [attributes setObject:[NSDate date] forKey:NSFileModificationDate];
    [[NSFileManager defaultManager] setAttributes:attributes ofItemAtPath:filePath error:NULL];

    return [NSData dataWithContentsOfFile:filePath];
}

- (void)storeImageData:(NSData *)data forURL:(NSString *)url {
    [[NSFileManager defaultManager] createFileAtPath:[self cacheFilePathForURL:url] contents:data attributes:nil];
}

Código do controlador de exibição de rolagem

Aqui está um trecho relevante do código que eu uso para exibir imagens em meus controladores de exibição de rolagem.

- (void)scrollViewDidScroll:(UIScrollView *)theScrollView {
    CGFloat pageWidth = theScrollView.frame.size.width;
    NSUInteger index = floor((theScrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;

    [self loadImageFor:[NSNumber numberWithInt:index]];
    [self loadImageFor:[NSNumber numberWithInt:index + 1]];
    [self loadImageFor:[NSNumber numberWithInt:index - 1]];
}

- (void)loadImageFor:(NSNumber *)index {
    if ([index intValue] < 0 || [index intValue] >= [self.photoData count]) {
        return;
    }

    // ... initialize an image loader object that accesses the disk image cache or makes a network request

    UIView *iew = [self.views objectForKey:index];    
    UIImageView *imageView = (UIImageView *) [view viewWithTag:kImageViewTag];
    if (imageView.image == nil) {
        NSDictionary *photo = [self.photoData objectAtIndex:[index intValue]];
        [loader loadImage:[photo objectForKey:@"url"]];
    }
}

O objeto do carregador de imagens é apenas um objeto leve que verifica o cache do disco e decide se deve ou não buscar uma imagem do disco ou da rede. Uma vez feito, ele chama um método no controlador de exibição de rolagem para exibir a imagem:

- (void)imageLoadedFor:(NSNumber *)index image:(UIImage *)image {
    // Cache image in memory
    // ...

    UIView *view = [self.views objectForKey:index];
    UIImageView *imageView = (UIImageView *) [view viewWithTag:kImageViewTag];
    imageView.contentMode = UIViewContentModeScaleAspectFill;
    imageView.image = image;
}

ATUALIZAR

Eu estava experimentando o aplicativo, desabilitei o cache da imagem e reverti para sempre fazer solicitações de rede. Parece que simplesmente usar solicitações de rede para buscar imagens écausando também o mesmo atraso ao rolar pelas visualizações de rolagem e de tabela! Ou seja, quando uma solicitação de rede é concluída e a imagem é mostrada na página de exibição de rolagem ou na célula da tabela, a interface do usuário fica um pouco atrasada e possui alguns segundos de atraso enquanto tento arrastá-la.

O atraso parece ser apenas mais perceptível ao usar o cache do disco, pois o atraso sempre ocorre logo na transição da página. Talvez eu esteja fazendo algo errado ao atribuir a imagem carregada ao UIImageView apropriado?

Além disso - tentei usar imagens pequenas (miniaturas 50x50) e o atraso parece melhorar. Parece que o impacto no desempenho se deve ao carregamento de uma imagem grande do disco ou ao carregamento de uma imagem grande em um objeto UIImage. Eu acho que uma melhoria seria reduzir o tamanho das imagens sendo carregadas na exibição de rolagem e na tabela, que era o que eu estava planejando fazer. No entanto, eu simplesmente não entendo como outros aplicativos de fotografia intensiva são capazes de apresentar o que parecem fotos de alta resolução em exibições roláveis sem problemas de desempenho ao acessar o disco ou a rede.

questionAnswers(4)

yourAnswerToTheQuestion