23

Ich habe eine Test-App-Setup und es erfolgreich Inhalte aus dem Netzwerk heruntergeladen, auch wenn der Benutzer wechselt Apps während ein Download läuft. Großartig, jetzt habe ich Hintergrund-Downloads an Ort und Stelle. Jetzt möchte ich Caching hinzufügen. Es hat keinen Sinn, Bilder mehr als einmal herunterzuladen, b/c des Systemdesigns. Angesichts einer Bild-URL kann ich Ihnen sagen, dass sich der Inhalt hinter dieser URL niemals ändern wird. Also, jetzt möchte ich die Ergebnisse meines Downloads mit dem eingebauten Cache/Speicher auf der Festplatte speichern, über den ich so viel gelesen habe (im Gegensatz zu mir, die Datei manuell in NSCachesDirectory zu speichern und dann dort nachzusehen, bevor ich neu mache) Bitte, ick). In einem Versuch Caching auf diesem Arbeits Code zu bekommen arbeiten, habe ich den folgenden Code:Wie wird mit NSURLSession und NSURLCache zwischengespeichert? Funktioniert nicht

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    // Override point for customization after application launch. 

    // Set app-wide shared cache (first number is megabyte value) 
    [NSURLCache setSharedURLCache:[[NSURLCache alloc] initWithMemoryCapacity:60 * 1024 * 1024 
                   diskCapacity:200 * 1024 * 1024 
                    diskPath:nil]]; 

    return YES; 
} 

Wenn ich meine Sitzung erstellen, habe ich zwei neue Linien (Urlcache und requestCachePolicy).

// Helper method to get a single session object 
- (NSURLSession *)backgroundSession 
{ 
    static NSURLSession *session = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.example.apple-samplecode.SimpleBackgroundTransfer.BackgroundSession"]; 
     configuration.URLCache = [NSURLCache sharedURLCache]; // NEW LINE ON TOP OF OTHERWISE WORKING CODE 
     configuration.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad; // NEW LINE ON TOP OF OTHERWISE WORKING CODE 
     session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; 
    }); 
    return session; 
} 

Dann, gerade extrem überflüssig in einem Versuch, das Caching Erfolg sehe ich aus

meine NSURLRequest Leitung geschaltet
// NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL]; // Old line, I've replaced this with... 
NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:2*60]; // New line 

Nun, wenn ich das Produkt ein zweites Mal zum Download gehen, die Erfahrung ist Exakt wie das erste !! Es dauert sehr lange, bis die Bar langsam und stetig animiert wird. Ich möchte die Daten im Cache sofort !! Was vermisse ich???

---------------------------- UPDATE ----------------- -----------

okay, dank Thorsten Antwort, habe ich die folgenden zwei Codezeilen zu meinem didFinishDownloadingToURL Delegatmethode hinzugefügt:

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)downloadURL { 

    // Added these lines... 
    NSLog(@"DiskCache: %@ of %@", @([[NSURLCache sharedURLCache] currentDiskUsage]), @([[NSURLCache sharedURLCache] diskCapacity])); 
    NSLog(@"MemoryCache: %@ of %@", @([[NSURLCache sharedURLCache] currentMemoryUsage]), @([[NSURLCache sharedURLCache] memoryCapacity])); 
    /* 
    OUTPUTS: 
    DiskCache: 4096 of 209715200 
    MemoryCache: 0 of 62914560 
    */ 
} 

Das ist großartig. Es bestätigt, dass der Cache wächst. Ich nehme an, da ich eine downloadTask (Downloads in Datei im Gegensatz zu Speicher) verwendet, ist das der Grund, warum DiskCache wächst und nicht zuerst Speichercache? Ich dachte mir, dass alles in den Speichercache gehen würde, bis das übergelaufen ist und dann der Plattencache verwendet wurde und vielleicht der Speichercache auf die Platte geschrieben wurde, bevor das Betriebssystem die App im Hintergrund zum Platzen bringt. Missverstehe ich, wie Apples Cache funktioniert?

Dies ist ein Schritt nach vorn für sicher, aber das zweite Mal, dass ich die Datei herunterladen es nur so lange, wie die erste Zeit in Anspruch nimmt (vielleicht 10 Sekunden oder so) und das folgende Verfahren erneut ausgeführt bekommen:

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite 
{ 
    // This shouldn't execute the second time around should it? Even if this is supposed to get executed a second time around then shouldn't it be lightning fast? It's not. 
    // On all subsequent requests, it slowly iterates through the downloading of the content just as slow as the first time. No caching is apparent. What am I missing? 
} 

Was machen Sie von meinen Bearbeitungen oben? Warum sehe ich die Datei bei nachfolgenden Anfragen nicht schnell?

Wie kann ich bestätigen, dass die Datei bei der zweiten Anforderung aus dem Cache geliefert wird?

Antwort

31

Beachten Sie, dass die folgenden SO Post hat mir geholfen, mein Problem zu lösen: Is NSURLCache persistent across launches?

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    // Set app-wide shared cache (first number is megabyte value) 
    NSUInteger cacheSizeMemory = 500*1024*1024; // 500 MB 
    NSUInteger cacheSizeDisk = 500*1024*1024; // 500 MB 
    NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"]; 
    [NSURLCache setSharedURLCache:sharedCache]; 
    sleep(1); // Critically important line, sadly, but it's worth it! 
} 

Neben der sleep(1) Linie, auch die Größe meines Cache beachten; 500 MB. Sie benötigen eine Cache-Größe, die viel größer ist als das, was Sie zwischenspeichern möchten. Wenn Sie beispielsweise ein 10-MB-Bild zwischenspeichern möchten, reicht eine Cachegröße von 10 MB oder sogar 20 MB nicht aus. Wenn Sie 10 MB Cache cachen müssen, würde ich einen 100 MB Cache empfehlen. Hoffe das hilft!! Hat den Trick für mich gemacht.

+0

Ich habe versucht, diesen Code zu verwenden, um zu sehen, ob kleine Thumbnails schneller zurückkommen würden, sobald sie einmal aus dem Netzwerk geladen wurden. Sie kommen nicht schneller rein. Ich frage mich, ob dieser Cache wirklich funktioniert. – zumzum

+1

@zumzum Sie können eine größere Datei versuchen, um zu sehen, ob sie funktioniert. Eine große Datei macht sehr deutlich, ob der Cache richtig eingerichtet ist und funktioniert. –

+6

Um etwas genauer zu sein, die Apple Dokumentation für URLSession: dataTask: willCacheResponse: completionHandler: '(https://developer.apple.com/library/prerelease/ios/documentation/Foundation/Reference/NSURLSessionDataDelegate_protocol/index. html # // apple_ref/occ/intfm/NSURLSessionDataDelegate/URLSession: dataTask: willCacheResponse: completionHandler :) gibt explizit an, dass NSURLSession nicht versucht, eine Datei mit mehr als 5% der Cache-Größe zwischenzuspeichern – jcaron

2

Sobald Sie den Cache und die Sitzung festgelegt, sollten Sie die Session-Methoden verwenden, um Ihre Daten herunterladen:

- (IBAction)btnClicked:(id)sender { 
    NSString *imageUrl = @"http://placekitten.com/1000/1000"; 
    NSURLSessionDataTask* loadDataTask = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:imageUrl]] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
     UIImage *downloadedImage = [UIImage imageWithData:data]; 
     NSLog(@"ImageSize: %f, %f", downloadedImage.size.width, downloadedImage.size.height); 
     NSLog(@"DiskCache: %i of %i", [[NSURLCache sharedURLCache] currentDiskUsage], [[NSURLCache sharedURLCache] diskCapacity]); 
     NSLog(@"MemoryCache: %i of %i", [[NSURLCache sharedURLCache] currentMemoryUsage], [[NSURLCache sharedURLCache] memoryCapacity]); 
    }]; 
    [loadDataTask resume]; //start request 
} 

Nach dem ersten Aufruf wird das Bild im Cache gespeichert.

+0

Vielen Dank für Ihre Antwort! Bitte beachten Sie die Änderungen, die ich zu meiner Frage oben vorgenommen habe. Meine Bearbeitungen enthalten Ihre Antwort. Ihr Code bestätigt, dass die Daten * im Cache gespeichert werden *, aber ich bin immer noch nicht in der Lage, die Daten für alle folgenden Anfragen aus dem Cache * zu erhalten *. Fehle ich etwas? –

+1

Versuchen Sie NSURLSessionDataTask anstelle von NSURLSessionDownloadTask. 4096 Bytes verwendeter Festplatten-Cache sieht wie kein Cache aus. Ich nehme an, NSURLSessionDownloadTask verwendet eine andere Caching-Richtlinie als NSURLSessionDataTask. – Thorsten

6

Lösung - zuerst alle Informationen bekommen u es brauchen so etwas wie dieses

- (void)loadData 
{ 
    if (!self.commonDataSource) { 
     self.commonDataSource = [[NSArray alloc] init]; 
    } 

    [self setSharedCacheForImages]; 

    NSURLSession *session = [self prepareSessionForRequest]; 
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[BaseURLString stringByAppendingPathComponent:@"app.json"]]]; 
    [request setHTTPMethod:@"GET"]; 
    __weak typeof(self) weakSelf = self; 
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
     if (!error) { 
      NSArray *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; 
      weakSelf.commonDataSource = jsonResponse; 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       [weakSelf updateDataSource]; 
      }); 
     } 
    }]; 
    [dataTask resume]; 
} 

- (void)setSharedCacheForImages 
{ 
    NSUInteger cashSize = 250 * 1024 * 1024; 
    NSUInteger cashDiskSize = 250 * 1024 * 1024; 
    NSURLCache *imageCache = [[NSURLCache alloc] initWithMemoryCapacity:cashSize diskCapacity:cashDiskSize diskPath:@"someCachePath"]; 
    [NSURLCache setSharedURLCache:imageCache]; 
} 

- (NSURLSession *)prepareSessionForRequest 
{ 
    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; 
    [sessionConfiguration setHTTPAdditionalHeaders:@{@"Content-Type": @"application/json", @"Accept": @"application/json"}]; 
    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; 
    return session; 
} 

Nachdem Sie jede Datei herunterladen müssen - in meinem Fall - Analyse der Antwort machen und Bilder herunterladen. Auch vor antrag Sie müssen überprüfen, ob Cache bereits Antwort auf Ihre Anfrage haben - so etwas wie dieses

NSString *imageURL = [NSString stringWithFormat:@"%@%@", BaseURLString ,sourceDictionary[@"thumb_path"]]; 
NSURLSession *session = [self prepareSessionForRequest]; 
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:imageURL]]; 
[request setHTTPMethod:@"GET"]; 

NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request]; 
if (cachedResponse.data) { 
    UIImage *downloadedImage = [UIImage imageWithData:cachedResponse.data]; 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     cell.thumbnailImageView.image = downloadedImage; 
    }); 
} else { 
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
    if (!error) { 
     UIImage *downloadedImage = [UIImage imageWithData:data]; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      cell.thumbnailImageView.image = downloadedImage; 
     }); 
    } 
}]; 
    [dataTask resume]; 
} 

Danach können Sie auch mit xCode Network Analyzer führen überprüfen.

Beachten Sie auch, wie mentionted von @jcaron und documented by Apple

NSURLSession wird nicht versuchen, eine Datei größer als 5% des Cache Größe

Ergebnis etwas cachen wie

enter image description here

Verwandte Themen