Domanda

Ho un UITableView composto da circa 10 UITableViewCells sottoclasse di nome TBPostSnapCell. Ogni cella, quando inizializzato, set di due delle sue variabili con UIImages scaricati via GCD o recuperati da una cache memorizzato nella cartella Documenti dell'utente.

Per qualche ragione, questo causa un ritardo notevole sul tableView e quindi interrompendo il UX dell'app e tavolo.

mi puoi dire come posso ridurre questo ritardo?

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];
        }];
        ...
    }

}

TBImageDownloader.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);
        });
    });
}
È stato utile?

Soluzione

Per prima cosa da provare è la conversione DISPATCH_QUEUE_PRIORITY_HIGH (aka LAVORO ONG più importante mai dimenticare tutto il resto) a qualcosa come DISPATCH_QUEUE_PRIORITY_LOW.

Se questo non risolvere il problema si potrebbe tentare di fare il traffico http via dispatch_sources, ma che è un sacco di lavoro.

Si potrebbe anche solo cercare di limitare il numero di in volo http recupera con un semaforo, il vero trucco sarà decidere qual è il limite migliore è come il numero "buono" dipenderà sulla rete, le CPU, e la pressione di memoria . Forse punto di riferimento 2, 4, e 8 con alcune configurazioni e vedere se c'è abbastanza modello di generalizzare.

Ok, consente di provare solo uno, sostituire il 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);
});

Lasciare il resto del codice come è. Questo è probabile che sia il meno sputtery, ma non può caricare le immagini molto veloce.

Per il caso più generale, strappare il cambiamento ho appena dato, e lavoreremo su questo:

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);
    });
});

La sostituzione 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 : non ho ancora testato niente di tutto questo codice, anche per vedere se si compila. Come scritto consentirà solo 2 thread per essere in esecuzione la maggior parte del codice nei due blocchi annidati. Si potrebbe desiderare di spostare l'fino dispatch_semaphore_signal alla linea commentato. Che limiterà a due recuperi / immagine crea, ma sarà permesso di sovrapposizione con la scrittura dei dati immagine in un file e chiamando il callback _block.

A proposito si fa un sacco di file di I / O che è più veloce su flash quindi qualsiasi disco sia mai stato, ma se siete ancora alla ricerca di vittorie di prestazioni che potrebbero essere un altro luogo per attaccare. Per esempio forse mantenere l'UIImage giro in memoria fino ad ottenere un avviso di memoria a bassa e solo allora li scrittura su disco.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top