2013-12-09 3 views
8

Я использую NSUserDefaults, чтобы сохранить, было ли принято приложение EULA и PP (между прочим). Это нормально работает. Я могу начать, выйти, а затем вернуться в приложение, и он считывает значение в порядке. Я могу убить приложение и перезапустить - читает настройки по умолчанию. Я могу перезагрузить телефон, а затем перезапустить приложение, и он отлично читает настройки по умолчанию.Почему NSUserDefaults не читается после разряженной батареи IOS7

Но когда телефон перезапускается из разряженной батареи, я открываю приложение, и мне будет предложено снова принять мое лицензионное соглашение. Это происходит только на моем iPhone5 на IOS7. У меня 3GS на IOS6, который не проявляет такого же поведения.

Я подозреваю, что это может быть аналогичная проблема с той, которая была решена here, но это относится к вопросам разрешения в цепочке ключей. Были ли те же проблемы с разрешениями применимы к NSUserDefaults?

У кого-нибудь возникли подобные проблемы на IOS7 с NSUserDefaults?

+0

Есть и другие подобные неотвеченные вопросы о SO. Похоже, может быть, неразрешенная ошибка. Вы можете обойти это, используя NSCoder или Core Data. –

ответ

21

Таким образом, после экспериментов и прибегая к помощи выводам я пришел к распределились следующим образом:

значительного изменение обновления и фактически любой процесс, который запускает приложение позади экрана блокировки проблемы здесь. Файл .plist (NSUserDefaults defaultUser) загружает защиту (NSFileProtectionCompleteUntilFirstUserAuthentication), так что он недоступен только после первой разблокировки после запуска приложения. Поэтому, если процесс запускает ваше приложение в фоновом режиме, и ваше приложение пытается получить доступ к умолчанию по умолчанию пользователя пользователя, он не может загрузить файл и поэтому дает вам новый пустой набор пользовательских значений по умолчанию.

Что произошло в моем случае, так это то, что приложение перешло в состояние ожидания, когда EULA и PP будут приняты, поскольку он считывает данные по умолчанию (которые не могут быть прочитаны), что они не были приняты все же. После разблокировки телефона и повторного открытия приложения, которое, пожалуйста, обратите внимание, уже «запущено» - есть некоторые процессы, которые пишут NSUserDefaults, некоторые в моем приложении и некоторые из библиотек, которые использует мое приложение. В большинстве случаев я вызывал синхронизацию по умолчанию, поэтому уничтожал старые значения по умолчанию, которые невозможно было прочитать. Полагаю, это может случиться для многих людей.

Существует несколько различных решений.

Сначала я написал класс, эквивалентный NSUserDefaults, обертывающий NSMutableDictionary и сохраняющий словарь в .plist в библиотеке/поддержке приложений. Я изменил защиту файла на NSFileProtectionNone. Обратите внимание, что это не рекомендуется, если вы храните конфиденциальную информацию в этом файле. Также обратите внимание, что вы должны устанавливать разрешения для файла каждый раз, когда вы его пишете. Что-то вроде:

NSError *error; 
BOOL saved = [defaultsDic writeToURL:defaultsFileUrl atomically:YES]; 
[[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObject:NSFileProtectionNone forKey:NSFileProtectionKey]ofItemAtPath:[defaultsFileUrl path] error:&error]; 

Этот метод работает хорошо, но, как выясняется, у меня был еще один вопрос, с данными, которые я читал и писал с брелка. См. Ссылку в моем вопросе выше, ее та же проблема. Значения keychain имеют одинаковую защиту вплоть до первой разблокировки после запуска приложения. Я не хотел удалять защиту из брелка, и на самом деле мне было не очень удобно снимать защиту с моих пользовательских настроек по умолчанию.

Итак, следующее решение - фактически решить проблему. Не пытайтесь получить доступ к защищенным данным, если приложение запущено за экраном блокировки! Это означает, что я должен обнаружить, что приложение запускается за экраном блокировки, а затем дождитесь, пока приложение будет разблокировано, прежде чем я начну читать мои значения по умолчанию и значения привязки для пользователя.

Первое требование - проверить запуск приложения, если доступны защищенные данные, возможно, в applicationDidLaunch или в другом месте.

[[UIApplication sharedApplication]isProtectedDataAvailable] 

Если это не так, когда приложение запущено, то вы находитесь за экраном блокировки. Вы должны остановиться на этом этапе и воздерживаться от любых операций, которые обращаются к NSUserDefaults или Keychain (или к любому защищенному файлу, если на то пошло!). Затем вам нужно дождаться сигнала о том, что защищенные данные стали доступными. AppDelegate получает следующее, когда пользователь открывает экран блокировки:

-(void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application 

После того, как вы получите, что вы можете продолжать выполнение приложения.

В моем случае я контролирую все в одном классе. Когда этот класс создаются (который происходит только при запуске приложения) я проверяю, если защищенные данные доступны или нет, и подписаться на NSNotificationCenter для того же уведомление:

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationProtectedDataDidBecomeAvailable) name:UIApplicationProtectedDataDidBecomeAvailable object:nil]; 

Так с этим вторым способом, проблема решена и данные остаются защищенными, и все довольны.

+0

Спасибо за ваш ответ, знаете ли вы, нормально ли, что файлы в NSDocumentDirectory ведут себя как NSUserDefaults, когда экран заблокирован? – franck

+0

@muldercnc благодарит за это. Если пользователь разблокирует свой экран, скажем, через 30 минут после того, как было обновлено фоновое обновление приложения, можем ли мы еще извлечь данные в фоновом режиме? (поскольку у нас меньше минуты до того, как ОС отключит нас). Что же делать с обработчиком завершения UIBackgroundFetchResult? – Serluca

+0

@ Serluca Я так считаю. Поскольку он генерирует событие, ваше приложение будет реагировать на событие и должно радостно сделать это в фоновом режиме даже через 30 минут. Конечно, вам придется попробовать это, чтобы подтвердить. Я использую консоль устройства в Xcode для контроля вывода XCODE => Window => Devices, используйте параметр консоли для тестируемого устройства (поскольку вы можете перезагрузить устройство во время отладки). – muldercnc

1

Это по-прежнему поведение на IOS 9.0 GM.

Смежные вопросы