23

我有一個測試應用程序設置,即使用戶在下載過程中切換應用程序,它也能成功從網絡下載內容。太棒了,現在我有了背景下載。現在我想添加緩存。我不止一次下載圖像,系統設計的B/C,給出一個圖像的URL我可以告訴你,該網址後面的內容將永遠不會改變。所以,現在我想使用蘋果內置的內置/磁盤緩存來緩存我下載的結果,我已經閱讀了很多內容(與我在NSCachesDirectory中手動保存文件然後在製作新內容之前檢查該文件相反)請求,ick)。在嘗試得到緩存在此工作的代碼頂部的工作,我添加以下代碼:如何使用NSURLSession和NSURLCache進行緩存。不工作

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

當我創建我的會議,我已經添加了兩條新線(Urlcache文件和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; 
} 

然後,僅僅是超冗餘,試圖看到緩存的成功,我打開我的NSURLRequest線從

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

現在,當我去下載項的第二次,經驗是像第一個一樣的exaclty!需要很長時間才能下載,並且進度條像原始下載一樣緩慢而穩定。我想立即在緩存中的數據!我錯過了什麼?

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

好了,感謝和Thorsten的回答,我已經添加的代碼下面兩行我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 
    */ 
} 

這是偉大的。它證實緩存正在增長。我推測,因爲我正在使用downloadTask(下載到文件而不是內存),那麼這就是爲什麼DiskCache正在增長,而不是第一次使用內存緩存?我認爲一切都會進入內存緩存,直到溢出,然後使用磁盤緩存,並且可能會在操作系統殺死後臺應用程序釋放內存之前將內存緩存寫入磁盤。我誤解了Apple的緩存工作原理嗎?

這是向前邁進了一步肯定的,但第二次我下載的文件它只需只要在第一時間(也許10秒左右)和下面的方法不會再次得到執行:

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

你對我上面的編輯做了什麼?爲什麼我在後續請求中看不到該文件很快返回?

如何確認文件是否從第二個請求的緩存中提供?

回答

31

注意,下面的SO後幫我解決我的問題: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! 
} 

除了sleep(1)線,也注意到我的緩存的大小; 500MB。您需要的緩存大小比您嘗試緩存的大得多。舉例來說,如果您想要緩存10MB的圖片,那麼10MB甚至20MB的緩存大小將不夠用。如果你需要能夠緩存10MB,我推薦100MB緩存。希望這可以幫助!!對我來說有詭計了。

+0

我試過使用此代碼來查看一旦從網絡加載至少一次,小縮略圖是否會更快回來。他們沒有進步得更快。我想知道這個緩存是否真的有效。 – zumzum

+1

@zumzum你可以嘗試一個更大的文件,看它是否工作。一個大文件很清楚緩存是否設置正確並正常工作。 –

+6

爲了更精確一點,蘋果公司的'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 :)明確指出NSURLSession不會嘗試緩存大於緩存大小5%的文件 – jcaron

2

一旦你設置緩存和會話,你應該使用會話的方法來下載數據:

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

第一個電話後,圖像緩存。

+0

謝謝您的回答!請參閱我對上述問題所做的修改。我的編輯包含您的答案。您的代碼確實確認數據正被*保存到緩存中,但我仍然無法從所有後續請求中的緩存中獲取數據。我錯過了什麼嗎? –

+1

嘗試NSURLSessionDataTask而不是NSURLSessionDownloadTask。 4096個使用磁盤緩存的字節看起來完全沒有緩存。我假設NSURLSessionDownloadTask使用與NSURLSessionDataTask不同的緩存策略。 – Thorsten

6

解決方案 - 首先獲得所有信息u需要它是這樣的

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

後,您需要下載每個文件 - 在我的情況 - 做出響應的解析和下載圖像。也使您需要檢查,如果緩存中已經有你的請求響應請求之前 - 這樣的事情

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

之後,你還可以檢查和Xcode網絡分析儀的結果。

另請注意,由@jcaron mentionted和documented by Apple

NSURLSession不會嘗試緩存文件大於5%的高速緩存大小 的

結果類似

enter image description here