Comment mettre en cache en utilisant NSURLSession et NSURLCache. Pas de travail

j'ai une installation d'application de test et il télécharge avec succès le contenu du réseau, même si l'utilisateur commute des applications alors qu'un téléchargement est en cours. Super, maintenant j'ai des téléchargements en place. Maintenant, je veux ajouter la mise en cache. Il n'y a aucun intérêt à ce que je télécharge des images plus d'une fois, b/c de la conception du système, étant donné une URL d'image, je peux vous dire que le contenu derrière cette URL ne changera jamais. Donc, maintenant je veux mettre en cache les résultats de mon téléchargement en utilisant la mémoire intégrée d'apple/la mémoire cache sur disque qui J'ai lu tellement de choses à ce sujet (plutôt que de sauvegarder le fichier manuellement dans NSCachesDirectory et d'y vérifier avant de faire une nouvelle requête, ick). Dans une tentative pour faire fonctionner la mise en cache sur ce code de travail, j'ai ajouté le code suivant:

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

lorsque je crée ma session, j'ai ajouté deux nouvelles lignes (URLCache et 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;
}

alors, juste pour être ultra redondant dans une tentative de voir la mise en cache succès j'ai changé ma ligne Nslrequest de

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

Maintenant, quand je vais pour télécharger une 2ème fois, l'expérience est exactement comme le premier!! Il faut beaucoup de temps pour télécharger et la barre de progression est animée lente et stable comme un téléchargement original. Je veux les données dans le cache immédiatement!! Ce qui me manque???

----------------------------mise à JOUR----------------------------

D'accord, grâce à la réponse de Thorsten, J'ai ajouté les deux lignes de code suivantes à ma méthode de délégation didFinishDownloadingToURL :

- (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
    */
}

c'est génial. Ça confirme que la cache grandit. Je présume que puisque j'utilise un downloadTask (téléchargements vers le fichier par opposition à la mémoire), c'est pour cela que DiskCache est en croissance et pas la mémoire cache en premier? J'ai pensé que tout irait dans la mémoire cache jusqu'à ce que ça déborde et que la mémoire cache serait utilisée et que peut-être la mémoire cache a été écrite sur le disque avant que L'OS ne tue l'application. en arrière plan pour libérer la mémoire. Est-ce que je me méprends sur le fonctionnement de la cache D'Apple?

c'est un pas en avant pour sûr, mais la deuxième fois que je télécharge le fichier il prend aussi longtemps que la première fois (peut-être 10 secondes ou plus) et la méthode suivante ne obtient exécuté à nouveau:

- (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?
}

Que pensez-vous de mes modifications ci-dessus? Pourquoi est-ce que je ne vois pas le dossier retourné très rapidement sur les demandes subséquentes?

Comment puis-je confirmer si le fichier est servi à partir du cache sur la 2e requête?

23
demandé sur John Erck 2014-02-22 20:58:54

3 réponses

notez que le suivant SO post m'a aidé à résoudre mon problème: est-ce que Nslcache est persistant à travers les lancements?

- (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!
}

en plus de la ligne sleep(1) , notez aussi la taille de mon cache; 500MB. Vous avez besoin d'une taille de cache qui est plus grand que ce que vous essayez de cache. Par exemple, si vous voulez pouvoir mettre en cache une image 10MB, alors une taille de cache de 10MB ou même 20MB ne sera pas suffisante. Si vous avez besoin d'être capable de mettre en cache 10MB, Je recommander un cache de 100 Mo. Espérons que cette aide!! A fait le tour pour moi.

31
répondu John Erck 2017-05-23 11:47:23

Solution-obtenir d'abord toutes les informations u besoin il quelque chose comme ça

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

après que vous avez besoin de télécharger chaque fichier - dans mon cas - faire parsing de la réponse et télécharger des images. Aussi, avant de faire une requête, vous devez vérifier si le cache a déjà une réponse pour votre requête - quelque chose comme ceci

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

après cela, vous pouvez également vérifier le résultat avec Xcode Analyseur de réseau.

note également mentionnée par @jcaron et documentée par Apple

NSURLSession ne tentera pas de mettre en cache un fichier supérieur à 5% du cache taille

résultat quelque chose comme

enter image description here

8
répondu gbk 2015-06-30 15:48:13

une fois que vous avez défini le cache et la session, vous devez utiliser les méthodes session-pour télécharger vos données:

- (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
}

Après le premier appel, l'image est mise en cache.

2
répondu Thorsten 2014-02-23 04:29:00