5

iOS7自带了NSURLSession,借助NSURLSessionConfigure,可以自定义URLCache ,所以我试了一下,但是没有运气,我的URLCache似乎根本没有使用。

- (void)testURLCache
{
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    config.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
    NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:1024 * 100 diskCapacity:1024 * 100 diskPath:@"test.urlcache"];

    NSLog(@"usage:%lu", (unsigned long)[cache currentDiskUsage]);

    config.URLCache = cache;

    NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
    for (int i = 0; i < 40; i++) {
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.min.js?%d", i]]];
        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            if (error) {
                NSLog(@"error:%@", error);
            } else {
                NSLog(@"done:%d", i);
            }
        }];
        [task resume];
        [NSThread sleepForTimeInterval:0.5];
    }
    NSLog(@"usage:%lu", (unsigned long)[cache currentDiskUsage]);
}

我观察到的东西:

1)在test.urlcache文件夹下创建一个文件Caches夹,里面有3个文件Cache.db,,,,大小为4KB Cache.db-shm,里面没有记录。Cache.db-walCache.db

2)每次运行代码,usage的第一次输出总是0,for循环结束后usage变成4096,下一次usage又变成0。

4

2 回答 2

5

似乎有两个潜在的问题:

  1. 你的缓存太小了。从历史上看,如果被缓存的项目超过总缓存大小的 10%,它就不会缓存。

  2. 您在最后一次请求后 0.5 秒检查磁盘使用情况。将缓存写入持久存储可能为时过早。

下面我使用更大的缓存并在最终检查磁盘使用情况之前等待 10 秒,并且缓存工作正常。我还使用 GCD 对串行任务进行排队,以消除任意 0.5 秒的延迟,这既更安全,又使其在通过网络检索数据和使用缓存时更加明显。

- (void)testURLCache
{
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    config.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;

    // this is too small and won't cache
    // NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:100 * 1024 diskCapacity:100 * 1024 diskPath:@"small.urlcache"];

    // use bigger cache
    NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:10 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:@"big.urlcache"];

    config.URLCache = cache;

    // I'm going to add tasks to serial queue so that (a) I don't block main queue;
    // and (b) I don't need arbitrary delay, but I can just signal when it's done.

    dispatch_queue_t queue = dispatch_queue_create("com.domain.app.datatasks", 0);

    NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
    for (int i = 0; i < 40; i++) {
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

        dispatch_async(queue, ^{
            NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.min.js?%d", i]]];
            NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

                if (error) {
                    NSLog(@"error:%@", error);
                } else {
                    NSLog(@"done:%d", i);
                }

                dispatch_semaphore_signal(semaphore);                   // signal dispatched block that request is done
            }];
            [task resume];

            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);  // wait until task is complete
        });
    }

    // when the queued tasks finish, then check disk usage both immediately and 10 seconds later

    dispatch_async(queue, ^{

        // check cache size immediately after the data tasks finish

        NSLog(@"usage:%lu", (unsigned long)[cache currentDiskUsage]);

        // check again in 10 seconds

        double delayInSeconds = 10.0;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            NSLog(@"usage:%lu", (unsigned long)[cache currentDiskUsage]);
        });
    });
}

顺便说一句,对于这样的网络任务,我通常使用操作队列而不是调度队列,然后将我的数据任务包装在并发NSOperation子类中。然后我可以使用依赖关系来决定最终的完成操作,但同时允许并发但也决定并发程度,但这似乎超出了原始问题的范围,我想尽可能简单。

于 2013-12-25T06:15:45.187 回答
1

这是iOS7的问题。使用 iOS8.1 它可以正常工作,我不确定 iOS8。请注意,您仍然可以使用 iOS7 SDK 编译您的应用程序,并且它可以在 iOS8.1 设备上正常工作。
但是,您仍然可以使用全局缓存,而不是分配给的缓存NSURLSessionConfiguration

NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:1024 * 100 diskCapacity:1024 * 100 diskPath:@"test.urlcache"];
[NSURLCache setSharedURLCache:cache];

顺便说一句,在 iOS8 中,类中有 3 个尚未记录的方法NSURLCache(实际上它是一个NSURLSessionTaskAdditions类别NSURLCache,请检查 NSURLCache.h 文件):

- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forDataTask:(NSURLSessionDataTask *)dataTask  
- (void)getCachedResponseForDataTask:(NSURLSessionDataTask *)dataTask completionHandler:(void (^) (NSCachedURLResponse *cachedResponse))completionHandler  
- (void)removeCachedResponseForDataTask:(NSURLSessionDataTask *)dataTask

...但是,如果您想更改缓存机制,则不必触摸它们;)

于 2014-11-18T08:43:56.623 回答