Pregunta

tengo un UITableView que consiste en aproximadamente 10 subclasificado UITableViewCellS llamado tbpostsnapcell. Cada celda, cuando se inicializa, establece dos de sus variables con UIImageS descargado a través de GCD o recuperado de un caché almacenado en el directorio de documentos del usuario.

Por alguna razón, esto está causando un retraso notable en la vista de tabla y, por lo tanto, interrumpe el UX de la aplicación y la tabla.

Por favor, ¿puedes decirme cómo puedo reducir este retraso?

tableView ... CellforrowatIndExpath:

if (post.postType == TBPostTypeSnap || post.snaps != nil) {

        TBPostSnapCell *snapCell = (TBPostSnapCell *) [tableView dequeueReusableCellWithIdentifier:snapID];

        if (snapCell == nil) {

            snapCell = [[[NSBundle mainBundle] loadNibNamed:@"TBPostSnapCell" owner:self options:nil] objectAtIndex:0];

            [snapCell setPost:[posts objectAtIndex:indexPath.row]];

            [snapCell.bottomImageView setImage:[UIImage imageNamed:[NSString stringWithFormat:@"%d", (indexPath.row % 6) +1]]];
        }

    [snapCell.commentsButton setTag:indexPath.row];
    [snapCell.commentsButton addTarget:self action:@selector(comments:) forControlEvents:UIControlEventTouchDown];
    [snapCell setSelectionStyle:UITableViewCellSelectionStyleNone];

    return snapCell;
}

Tbsnapcell.m

- (void) setPost:(TBPost *) _post {

    if (post != _post) {
        [post release];
        post = [_post retain];
    }
    ...

    if (self.snap == nil) {

        NSString *str = [[_post snaps] objectForKey:TBImageOriginalURL];
        NSURL *url = [NSURL URLWithString:str];
        [TBImageDownloader downloadImageAtURL:url completion:^(UIImage *image) {
            [self setSnap:image];
        }];
    }

    if (self.authorAvatar == nil) {
        ...
        NSURL *url = [[[_post user] avatars] objectForKey:[[TBForrstr sharedForrstr] stringForPhotoSize:TBPhotoSizeSmall]];

        [TBImageDownloader downloadImageAtURL:url completion:^(UIImage *image) {
            [self setAuthorAvatar:image];
        }];
        ...
    }

}

TBIMageGoWownloader.m

+ (void) downloadImageAtURL:(NSURL *)url completion:(TBImageDownloadCompletion)_block {

    if ([self hasWrittenDataToFilePath:filePathForURL(url)]) {
        [self imageForURL:filePathForURL(url) callback:^(UIImage * image) {
            _block(image); //gets UIImage from NSDocumentsDirectory via GCD
        }];
        return;
    }

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_async(queue, ^{
        UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
        dispatch_async(dispatch_get_main_queue(), ^{
            [self writeImageData:UIImagePNGRepresentation(image) toFilePath:filePathForURL(url)];
            _block(image);
        });
    });
}
¿Fue útil?

Solución

Lo primero que debe probar es convertir el despate_queue_priority_high (también conocido como el trabajo más importante que jamás haya olvidado todo lo demás) a algo como Dispatch_queue_priority_low.

Si eso no lo soluciona, podría intentar hacer el tráfico HTTP a través de Dispatch_sources, pero eso es mucho trabajo.

También puede intentar limitar el número de recuperaciones de http en vuelo con un semáforo, el verdadero truco decidirá cuál es el mejor límite, ya que el "buen" número dependerá de la red, sus CPU y presión de memoria. Tal vez Benchmark 2, 4 y 8 con algunas configuraciones y ver si hay suficiente patrón para generalizar.

Ok, intentemos solo uno, reemplace el queue = ... con:

static dispatch_once_t once;
static dispatch_queue_t queue = NULL;
dispatch_once(&once, ^{
    queue = dispatch_queue_create("com.blah.url-fetch", NULL);
});

Deje el resto del código tal como está. Es probable que esto sea el menos pulverizado, pero puede no cargar las imágenes muy rápido.

Para el caso más general, arranca el cambio que le acabo de dar, y trabajaremos en esto:

dispatch_async(queue, ^{
    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self writeImageData:UIImagePNGRepresentation(image) toFilePath:filePathForURL(url)];
        _block(image);
    });
});

Reemplazándolo con:

static dispatch_once_t once;
static const int max_in_flight = 2;  // Also try 4, 8, and maybe some other numbers
static dispatch_semaphore_t limit = NULL;
dispatch_once(&once, ^{
    limit = dispatch_semaphore_create(max_in_flight);
});
dispatch_async(queue, ^{
    dispatch_semaphore_wait(limit, DISPATCH_TIME_FOREVER);
    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
    //  (or you might want the dispatch_semaphore_signal here, and not below)
    dispatch_async(dispatch_get_main_queue(), ^{
        [self writeImageData:UIImagePNGRepresentation(image) toFilePath:filePathForURL(url)];
        _block(image);
        dispatch_semaphore_signal(limit);
    });
});

NOTA: No he probado ninguno de este código, incluso para ver si se compila. Como escrito solo permitirá que 2 subprocesos ejecutaran la mayor parte del código en sus dos bloques anidados. Es posible que desee mover el desplazamiento_semaphore_signal a la línea comentada. Eso lo limitará a dos fetches/imagen crea, pero se les permitirá superponerse al escribir los datos de la imagen en un archivo y llamar a su devolución de llamada de _block.

Por cierto, haces muchas E/S de archivos que son más rápidas en Flash, entonces cualquier disco lo fue, pero si todavía estás buscando victorias de rendimiento que podrían ser otro lugar para atacar. Por ejemplo, tal vez mantener la UIImage en la memoria hasta que obtenga una advertencia de memoria baja y solo entonces escriba en disco.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top