Frage

Ich habe ein UITableView bestehend aus rund 10 unterklassigen UITableViewCellS genannt Tbpostsnapcell. Jede Zelle legt bei der Initialisierung zwei seiner Variablen mit UIImages heruntergeladen über GCD oder abgerufen von einem Cache, der im Dokumentenverzeichnis des Benutzers gespeichert ist.

Aus irgendeinem Grund führt dies zu einer bemerkenswerten Verzögerung auf der Tabelle und stört daher die UX der App & Tabelle.

Können Sie mir bitte sagen, wie ich diese Verzögerung reduzieren kann?

TABLEVIEW ... CELLFROWATInDEXPATH:

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);
        });
    });
}
War es hilfreich?

Lösung

Das erste, was Sie ausprobieren sollten, ist die Konvertierung von isspatch_queue_Priority_High (auch bekannt als die wichtigste Arbeit, die jemals alles andere vergessen hat) in so etwas wie isspatch_queue_priority_low.

Wenn das nicht repariert wird, können Sie versuchen, den HTTP -Verkehr über issatch_sources durchzuführen, aber das ist eine Menge Arbeit.

Sie können auch nur versuchen, die Anzahl der in Flug -HTTP -Abrufen mit einem Semaphor zu begrenzen. Der eigentliche Trick wird bestimmen, was die beste Grenze ist, da die "gute" Nummer vom Netzwerk, Ihrem CPUs und Ihrem Speicherdruck abhängt. Vielleicht Benchmark 2, 4 und 8 mit ein paar Konfigurationen und prüfen Sie, ob es genügend Muster gibt, um zu verallgemeinern.

OK, versuchen wir es nur mit einem, ersetzen Sie die queue = ... mit:

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

Lassen Sie den Rest des Codes so wie es ist. Dies ist wahrscheinlich am wenigsten sputterig, lädt die Bilder jedoch möglicherweise nicht sehr schnell.

Für den allgemeineren Fall riss die Änderung aus, die ich dir gerade gegeben habe, und wir werden daran arbeiten:

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

Ersetzen durch:

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

HINWEIS: Ich habe keinen dieser Code getestet, auch um zu sehen, ob er kompiliert. Wie geschrieben ermöglicht es nur 2 Threads, den Großteil des Code in Ihren beiden verschachtelten Blöcken auszuführen. Möglicherweise möchten Sie die Issemaphore_signal in die kommentierte Zeile verschieben. Dadurch werden Sie auf zwei Abrufen/Bild erstellt, die erstellt werden, aber sie können sich überlappen, wenn Sie die Bilddaten in eine Datei schreiben und Ihren _Block -Rückruf aufrufen.

Übrigens machen Sie eine Menge Datei -E/O, was auf Flash schneller ist, als jede Festplatte jemals, aber wenn Sie immer noch nach Leistungsiegern suchen, die möglicherweise ein weiterer Ort zum Angriff sind. Zum Beispiel vielleicht die Benutzeroberfläche im Gedächtnis behalten, bis Sie eine geringe Speicherwarnung erhalten und sie erst dann auf die Festplatte schreiben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top