Почему NSUserDefault не читается после разряженной батареи IOS7

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

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

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

Кто-нибудь сталкивался с подобными проблемами на IOS7 с помощью NSUserDefaults?

Ответ 1

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

Значительное изменение обновления и фактически любой процесс, запускающий приложение за экраном блокировки, является проблемой здесь. Файл .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];

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

Ответ 2

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