Кэширование данных с помощью NSCoding?

В моем приложении я получаю данные URL-адреса с сервера. В качестве оптимизации я нормализую данные, сохраняя их в пользовательском классе, чтобы впоследствии можно было быстро получить к ним доступ. Однако мне нужно хранить экземпляр этого класса локально и в автономном режиме, чтобы избежать получения данных с сервера, если я уже это сделал. Кроме того, веб-сайт меняется ежедневно, поэтому кешированные данные полезны только в том случае, если они относятся к одному и тому же дню.

Этот сценарий применяется к 2 отдельным классам (всего 4 объекта), и я попытался сохранить эти данные с помощью NSCoding. (До сих пор я пытался реализовать только 1 объект, но я планирую создать отдельный docPath для каждого объекта)

- (void) saveStateToDocumentNamed:(NSString*)docName
{
    NSError       *error;
    NSFileManager *fileMan = [NSFileManager defaultManager];
    NSArray       *paths   = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString      *docPath = [paths[0] stringByAppendingPathComponent:docName];

    if ([fileMan fileExistsAtPath:docPath])
        [fileMan removeItemAtPath:docPath error:&error];

    // Store the hours data
    Hours *h = [self parseHoursFromServer];
    NSDictionary *state = [NSDictionary dictionaryWithObjectsAndKeys:h, @"Hours", nil];

    // There are many ways to write the state to a file. This is the simplest
    // but lacks error checking and recovery options.
    [NSKeyedArchiver archiveRootObject:state toFile:docPath];
    NSLog(@"end");
}

- (NSDictionary*) stateFromDocumentNamed:(NSString*)docName
{
    NSError       *error;
    NSFileManager *fileMan = [NSFileManager defaultManager];
    NSArray       *paths   = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString      *docPath = [paths[0] stringByAppendingPathComponent:docName];

    if ([fileMan fileExistsAtPath:docPath])
        return [NSKeyedUnarchiver unarchiveObjectWithFile:docPath];

    return nil;
}

Однако, когда я попытался запустить этот код, я получил [Hours encodeWithCoder:]: unrecognized selector sent to instance 0, потому что мой класс в настоящее время не поддерживает NSCoding. После чтения как мне нужно вручную кодировать все свойства для моего пользовательского класса, я хочу убедиться, что NSCoding является идеальным решением моей проблемы с кэшированием данных.

РЕДАКТИРОВАТЬ:

Вот как я сейчас создаю URL

NSURL *url = [NSURL URLWithString:urlString];
NSData *pageData = [NSData dataWithContentsOfURL:url];

person Mahir    schedule 31.07.2014    source источник
comment
Вы знаете, что это уже кешируется, верно? developer.apple.com/ библиотека/ios/documentation/Cocoa/Reference/   -  person quellish    schedule 31.07.2014
comment
И если вы кэшируете что-то в файловой системе, используйте NSCachesDirectory, а не NSDocumentDirectory. Также рекомендуется создать собственный подкаталог в этом каталоге.   -  person quellish    schedule 31.07.2014
comment
Возможно, я подхожу к этому неправильно, но я подумал, что было бы лучше хранить пользовательский класс, а не только необработанные данные сервера.   -  person Mahir    schedule 31.07.2014
comment
Процесс извлечения данных и передачи их в класс занимает значительное количество кода, поэтому я надеялся устранить и этот процесс с помощью кэширования.   -  person Mahir    schedule 31.07.2014
comment
Если я не ошибаюсь, вам все равно придется это сделать. Если вы просто позволяете системе загрузки URL выполнять свою работу, вам не нужно внедрять и тестировать NSCoding и настраиваемую реализацию кэширования поверх этого.   -  person quellish    schedule 31.07.2014
comment
Чую преждевременную оптимизацию. сколько времени вы теряете на восстановление объекта из кеша? 1 наносекунда? 10? все еще слишком быстро для ваших нейронов. и чтение с диска может быть намного медленнее.   -  person vikingosegundo    schedule 31.07.2014
comment
@vikingosegundo Так вы тоже за кеширование только данных сервера, а не объекта?   -  person Mahir    schedule 31.07.2014
comment
@quellish Раньше я заглядывал в NSURLCache, но не был уверен, смогу ли я проверить, относятся ли кэшированные данные к текущей дате.   -  person Mahir    schedule 31.07.2014
comment
@Mahir Махир, я бы посоветовал вам сделать шаг назад и определить цель вашего местного магазина. Если это просто кеширование, то механизм кеша по умолчанию может быть в порядке (хотя он может быть дерганым, т. Е. Он применяет некоторые плохо документированные критерии, чтобы решить, действительно ли он будет кешировать или нет, вам нужно убедиться, что вы в порядке с сервер, управляющий кэшированием). Но если ваш локальный магазин должен, например, показывать пользователю последнее известное состояние, пока выполняется новый сетевой запрос, то я думаю, что было бы ошибкой искажать NSURLCache для этой цели, и что-то вроде NSCoding имеет смысл.   -  person Rob    schedule 31.07.2014


Ответы (1)


TL;DL; Система уже кэширует для вас!

Когда запрос сделан, система загрузки URL-адресов проверит общий кеш URL-адресов, чтобы увидеть, есть ли в кеше допустимый соответствующий ответ. Если есть, он будет использовать его, а не устанавливать сетевое соединение. Это прозрачно для вашего сетевого кода, он работает одинаково независимо от того, получают ли он данные из кеша или из сети. Вам не нужно ничего реализовывать, кроме как сказать NSURLCache использовать диск:

NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:(4 * 1024 * 1024) diskCapacity:(20 * 1024 * 1024) diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];

Это позволит ему использовать 4 МБ ОЗУ, 20 МБ на диске и расположение кеша по умолчанию на диске. Это нужно сделать один раз, когда ваше приложение запускается. С этого момента NSURLCache будет записывать любые кешируемые сетевые ответы в память, а также на диск. По мере истечения срока их действия или нехватки места в кеше они будут удалены. То, как сетевое соединение использует кэш, определяется параметром cachePolicy NSURLRequest. По умолчанию NSURLRequestUseProtocolCachePolicy использует кеш в соответствии с правилами протокола (HTTP). Ответы HTTP включают информацию о том, как долго ответ должен считаться действительным, и эта политика подчиняется этим правилам. По большей части это работает хорошо, если сервер, с которым вы общаетесь, неправильно реализует кэширование HTTP-ответов. Если вы хотите изменить политику кэширования, чтобы НЕ позволять системе загрузки URL-адресов пытаться загружать что-либо из сети, делайте это, ТОЛЬКО если радиоприемники устройства отключены. Если устройство находится в режиме полета, вы должны создать NSURLRequest с другой политикой кэширования (при условии, что вы используете Reachability для определения состояния сети):

if (networkStatus == NotReachable){
    request = [[NSURLRequest alloc] initWithURL:someURL cachePolicy: NSURLRequestReturnCacheDataDontLoad timeoutInterval:timeout];
} else {
    request = [[NSURLRequest alloc] initWithURL:someURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:timeout];
}

Это будет использовать политику NSURLRequestReturnCacheDataDontLoad, если устройство находится в режиме полета или ни сотовая связь, ни Wi-Fi не имеют сигнала. NSURLRequestReturnCacheDataDontLoad будет загружать ответы из кеша (даже если срок действия истек) и не пытаться получить доступ к сети для элементов, которых нет в кеше.

Подробнее об этом можно прочитать в Руководство по программированию системы загрузки URL: понимание доступа к кэшу

person quellish    schedule 31.07.2014
comment
Итак, похоже, что я могу определить кэшировать или нет с помощью connection:willCacheResponse:, но я все еще не уверен, как проверить дату кэшированных данных. Могу ли я просто сохранять дату в NSUserDefaults каждый раз, когда я обновляю кеш? - person Mahir; 31.07.2014
comment
Нет, этот метод сообщает делегату, что он будет кэшировать ответ. Для того, что вы хотите, вы не должны это реализовывать. Пусть NSURLCache выполняет свою работу, как описано в ответе. Это все, что вам нужно сделать. - person quellish; 31.07.2014
comment
Итак, проверяет ли он автоматически актуальность кэшированных данных? - person Mahir; 31.07.2014
comment
Да, он проверяет, действителен ли кешированный ответ. - person quellish; 31.07.2014
comment
Я попытался добавить NSURLCache *URLCache, как вы описали в своем ответе, но он по-прежнему обращается к серверу вместо загрузки данных из кеша. Я добавил подробности в свой вопрос, показывающий, как я инициализирую URL-адрес, также мне интересно, ограничено ли кэширование для iOS. - person Mahir; 01.08.2014
comment
Откуда вы знаете, что он связывается с сервером (Чарльз и т. д.)? Сервер говорит клиенту ничего не кэшировать? - person quellish; 02.08.2014
comment
Извините, я не слежу - person Mahir; 02.08.2014