Сотни обращений к «InputModeProperties.plist» приводят к задержке моей игры (iPhone)

#ios #objective-c #uikit #nsuserdefaults #property-list

#iOS #objective-c #uikit #nsuserdefaults #список свойств

Вопрос:

У меня странная проблема с исправлением ошибки для Tiny Wings. В своей игре я использую что-то вроде:

 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];      
[userDefaults setFloat:musicVolume forKey:@"musicVolume"];
  

для сохранения некоторых настроек и таблицы рекордов. В конце игры, когда появляется экран gameover, игра сохраняет рекорды в стандарте UserDefaults. Это работало очень хорошо, пока игра не отобразила UIAlertView, подобный этому:

 UIAlertView *alert = [[UIAlertView alloc] init];
[alert setTitle:@"Get ready!"];
[alert setDelegate:self];
[alert addButtonWithTitle:@"Ok"];
[alert show];
[alert release];
  

После того, как AlertView исчезал каждый раз, когда игра сохраняла что-либо в standardUserDefaults, игра на некоторое время зависала (на некоторых устройствах на несколько секунд). Это также происходит после того, как игра использовала UITextField для ввода имени игрока. До использования одного из двух элементов UIKit в игре нет никаких задержек, но после их использования игра зависает, пока я не перезапущу приложение. Я проанализировал проблему с инструментами производительности, и инструмент «Активность ввода-вывода» показывает, что существуют сотни обращений «открыть — прочитать — закрыть» к

 /System/Library/Frameworks/UIKit.framework/InputModeProperties.plist
  

что вызывает задержки.

Я совершенно не знаю, что делать. Есть идеи?

Редактировать:
на форуме разработчиков Apple есть темаhttp://devforums.apple.com/message/424374#424374 там, где у кого-то такая же проблема, и кажется, что она появляется только с iOS 4.3. Я протестировал это, и задержки происходят только на моих устройствах 4.3 (не на iPod Touch 3.1 и iPad 4.2).

Комментарии:

1. вы вызываете синхронизацию после каждого сохранения?

2. нет, я вызываю «синхронизировать» только тогда, когда приложение перестает быть активным.

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

4. Я только что просмотрел InputModeProperties.plist, похоже, там есть только данные о локализации, то есть о том, какой тип клавиатуры ожидать или использовать.

5. Нам нужно больше кода. Этого недостаточно, чтобы помочь вам с вашей проблемой. 🙂 Также поздравляю с этой игрой. Вы проделали феноменальную работу для группы из одного человека. 🙂

Ответ №1:

Редактировать

Приличное решение проблемы :

Краткая версия: Просто отложите вызовы, вызывающие ошибку, до тех пор, пока пользователь не перестанет раздражаться.

Длинная версия:

Поскольку я думаю, что проблема возникает из-за [NSUserDefaults standardUserDefaults] вызова, который запускает этот грязный цикл загрузки списка, ПОСЛЕ некоторого действия, запрашивающего раскладки клавиатуры (например, UIAlert )…

Я бы посоветовал вызывать [NSUserDefaults standardUserDefaults] только один раз при загрузке приложения (ПЕРЕД любым вызовом, вызывающим ошибку), и сохранять возвращаемую ссылку в классе singleton в течение всего жизненного цикла приложения. Я не думаю, что объем памяти будет огромным… (Я делаю это в нескольких приложениях без каких-либо проблем). В худшем случае загрузка plist * 100 будет выполнена только один раз при загрузке приложения, а не во время игры.

Если проблема возникает из-за [userDefaults setXxxx:...] вызовов, тот же обходной путь, вы могли бы просто сохранить значения для сохранения в памяти и установить их позже в userDefaults , например, непосредственно перед их синхронизацией… Но с риском потери информации, если что-то пойдет не так, например, сбой. Лично я предпочитаю делать это sync после каждого set , чтобы обеспечить целостность данных…

ПРАВКА КОНЦА


Краткий ответ: ошибка в iOS4.3, очень мало шансов найти обходной путь … отправьте сообщение об ошибке и дождитесь следующего обновления iOS… WWDC через 2 недели … 1-2 месяца.

Длинный:

После просмотра сборки UIKit, вот мои догадки:

  • InputModeProperties.plist содержит список всех раскладок клавиатур по локали.
  • UIKit используйте это для нескольких целей, например, при отображении клавиатуры, чтобы определить доступные раскладки клавиатуры. (Локали …)
  • Интересно одно, мы можем найти некоторую информацию о ней в NSUserDefaults :

     NSLog(@"%@", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]);
    ==> {
    AppleKeyboards =     (          // I have two keyboard in preferences
       "fr_FR@hw=French;sw=AZERTY", // french  first
       "en_US@hw=US;sw=QWERTY"      // english second
    );
    ...
      
  • Но эта информация не сохраняется в настройках приложений, в отличие от вашей оценки. ( NSGlobalDomain или, что более вероятно, Отдельные домены для каждого из предпочитаемых пользователем языков)
  • Поэтому я не был бы удивлен, что в UIKit NSUserDefaults существует конфликт (ошибка), вызывающий этот грязный цикл загрузки plist.
  • Вы говорите, около 100 вызовов? Это примерно столько же, сколько локалей / макетов в списке!

Когда клавиатура недоступна в NSUserDefaults … (Например, после синхронизации, давайте представим ошибку, делающую это)… UIKit можно было бы попробовать все доступные клавиатуры, чтобы определить пользовательскую, грязно проанализировав этот список 4.4 Тыс. раз… Например, при отображении UIAlertView … после NSUSerDefault синхронизации / изменения.

Кто знает? Ребята из Apple, у которых есть исходный код 🙂

Я бы не удивился, если бы зашел в настройки, чтобы установить клавиатуру, отличную от используемой по умолчанию, а затем вернуться к US, чтобы решить проблему. Бесполезно в вашем случае, но подтвердило бы проблему. Видел это из-за другой ошибки 4.3…

Как говорили другие люди, не использование NSUserDefaults, а простой пользовательский список в / Documents может быть (в) достойным обходным путем.

Отличная работа над Tiny Wings ! 🙂

Комментарии:

1. Задержка вызовов не решит проблему, потому что это просто вызовет ошибку во время игрового процесса, что хуже для пользователей старых устройств.

2. @Moshe цитирует Андреаса «я вызываю синхронизацию только тогда, когда приложение перестает быть активным». Я уже не говорю о слепой 1С второй проанализировать withdelay или что-то подобное, я говорю об ожидании значительного пользователя действий, например, бросить игру или… Когда он уходит в отставку, активный, как он уже делает.

3. спасибо за ваши предложения. я перепробовал все из них, но ни один не решает проблему, поэтому я просто больше не использую NSUserDefaults, как вы предложили, и это работает. Спасибо!

Ответ №2:

Хорошо, оглядываясь вокруг, кажется, что InputModeProperties.plist — это просто список аппаратных и программных клавиатур.

Глядя на ветку форума, которую вы опубликовали, эта проблема, по сути, заключается в том, что после загрузки объекта UITextField или UIAlertView (который включает UITextInputTraits.h среди прочего) всякий раз, когда вы пытаетесь сохранить пользовательские настройки по умолчанию, возникает необъяснимый цикл в файле определений клавиатур. Это происходит только в iOS 4.3.

Мне это кажется ужасно похожим на ошибку в UIKit, и я предполагаю, что внезапно UIKit сохраняет множество вещей без реальной цели. В этом случае может быть сложно что-либо с этим сделать, хотя вы могли бы избежать этих элементов (не так уж плохо для предупреждения, но текстовое поле будет сложнее) или вы могли бы переключиться на core data. В качестве альтернативы, вы могли бы создать изменяемый словарь для всех ваших опций и сохранить его по умолчанию для пользователя, когда приложение закрывается, и вас меньше волнует пауза. Или просто дождитесь обновления.

Удачи (кстати, игра мне нравится)

Ответ №3:

Совместное использование UIKit и OpenGL не рекомендуется. Я не думаю, что проблема заключается не столько в этом представлении, сколько в концепции смешивания двух. Я настоятельно рекомендую удалить это предупреждение и вместо этого показать пользовательское наложение, чтобы вы могли выполнить две вещи:

  1. Сделайте так, чтобы предупреждение «Приготовиться» соответствовало графике игры.
  2. Полностью избегайте этой проблемы.

Я наблюдал низкую производительность с текущей версией в App Store при возобновлении игры на iPod touch второго поколения.

Если вы хотите сохранить эти элементы как есть, в этом сообщении на форуме разработчиков Apple предлагается запустить синхронизацию в отдельном потоке. Исходя из этого, я бы предложил следующие шаги:

  1. Как предлагалось другим, сохраните где-нибудь ссылку на NSUserDefaults. plist. (Обычно я просто делаю что-то вроде этого: #define kSettings [NSUserDefaults standardUserDefaults] . Конечно, вам нужно вызвать его один раз, чтобы создать экземпляр singleton.)

  2. Запустите synchronize вызов во втором потоке (согласно сообщению на форуме разработчиков Apple).

  3. Посмотрите, можете ли вы вызвать synchronize в другое время, чем willResignActive . Проблема кажется немного хуже, когда вы вызываете synchronize из этого метода.

Поздравляю с игрой.

Ответ №4:

Просто бросаю взгляд в темноту, глядя на разницу в 4.3 —

Возможно, сочетание нового

 - (BOOL)disablesAutomaticKeyboardDismissal
  

в UIViewController и UIModalPresentationFormSheet (где по умолчанию используется значение YES) вызывает какой-то цикл в вашем коде.

Ответ №5:

Предполагая, что вы выполняете метод для отображения представления предупреждений, пробовали ли вы следующее?

 [self performSelector:@selector(displayAlert) withObject:nil afterDelay:0.5];

- (void)displayAlert {
  UIAlertView *alert = [[UIAlertView alloc] init];
  [alert setTitle:@"Get ready!"];
  [alert setDelegate:self];
  [alert addButtonWithTitle:@"Ok"];
  [alert show];
  [alert release];
}
  

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

Ответ №6:

Вероятно, это сохраняет каждый ваш параметр как отдельную транзакцию. Я не уверен. Вы могли бы попробовать использовать свой собственный одноэлементный класс DataStorage с NSMutableDictonairy в качестве хранилища данных. И синхронизируйте ее с NSUserDefaults на applicationDidEnterBackground: и applicationWillTerminate: . Или даже если вы не использовали системные настройки со своими ошибками NSUserDefaults — вы могли бы сохранить этот NSMutableDictonairy следующим образом:

 tempData = [NSMutableData data];
archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:tempData];
[archiver encodeObject:mutableDict forKey:@"data"];
[archiver finishEncoding];
result = [tempData writeToFile:archivePath atomically:YES];
[archiver release];
  

p.s. большой палец вверх за крошечные крылья. 😉