Hundreds of accesses to "InputModeProperties.plist" are lagging my game (iPhone)
Asked Answered
F

6

22

I have a weird problem with a bugfix for Tiny Wings. In my game i use something like:

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

for saving some preferences and the highscore table. At the end of the game when the gameover screen appears the game saves the highscores to the standardUserDefaults. It works very well until the game displayed an UIAlertView like this:

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

After the AlertView disappeared everytime the game save somthing to the standardUserDefaults the game lags for a while (on some devices for several seconds). This also happens after the game used an UITextField for inputting the player name. There is not any lag in the game before one of the two UIKit Elements are used but after the use of them the game lags until i restart the app. I have analysed the problem with the Performance Tools and the "I/O Activity" Instrument shows that there are hundreds of "open - read - close" accesses to the

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

which causes the lags.

I totaly have no clue what to do. Any ideas?

Edit:
there is a thread in the apple developer forum http://devforums.apple.com/message/424374#424374 where somebody has an equal problem and it seems that it only appears with iOS 4.3. I have tested it and the lags only happens on my 4.3 devices (not on a 3.1 iPod Touch and 4.2 iPad).

Foster answered 16/5, 2011 at 22:40 Comment(8)
are you calling -synchronize after every save?Dowski
no, i'm only calling -synchronize when the app resign active.Foster
well that isn't your problem then, I would guess that the alert isn't relevant, it is probably what you are doing when the alert is dismissed. you may want to synchronize more frequently (after a set of saved values) so you don't lose that data if your app dies for some reason (no fault of your own of course).Dowski
I just looked over the InputModeProperties.plist, looks to just have localization data, i.e. what type of keyboard to expect or use.Dowski
We need more code. This isn't enough to help you with your problem. :) Also, congrats on this game. You did a phenomenal job for a one-man band. :)Exceptional
yes i know i have to synchronize more frequently. i deleted all the synchronize calls because i thought they are causing the lag. I think the lags really have to do with the AlertView, there is a thread in the apple developer forum [link]devforums.apple.com/message/424374#424374 where somebody has an equal problem and it seems that it only appears with iOS 4.3. I have tested it and the lags only happens on my 4.3 devices (not on a 3.1 iPod Touch and 4.2 iPad).Foster
Does it help to call [NSUserDefaults synchronize] on a background thread?Iorgos
Personally, I'm not such a fan of that alert altogether, it's terribly laggy on second generation devices.Euphorbiaceous
R
3

EDIT

A decent bug workaround :

Short version: Just delay the bug-triggering calls until user is not annoyed.

Long version:

Since I think the issue is coming from [NSUserDefaults standardUserDefaults] call, that triggers that dirty plist-loading loop AFTER some action requesting Keyboard layouts (like an UIAlert)...

I would suggest to call [NSUserDefaults standardUserDefaults] just one time at app loading (BEFORE any bug-causing call), and keep the returned reference in a singleton class during all app lifecycle. I don't think the memory footprint would be huge... (I'm doing this in several apps w/o any issues). At worse the plist load*100 would be done only once at app load, and not during game.

If the issue comes from [userDefaults setXxxx:...] calls, same workaround, you could just keep values to save in memory and set them later in userDefaults, like just before syncing them... But at the risk of losing informations if anything goes wrong, like a crash. I personally prefer to sync after each set to ensure data integrity...

ENDOFEDIT


The short answer : iOS4.3 bug, very few chances to find a workaround... bugreport and wait for next iOS update... WWDC in 2 weeks... 1~2 month.

The long one:

After looking at UIKit asssembly, here are my guesses:

  • InputModeProperties.plist contains a list of all keyboards layouts by locale.
  • UIKit use this for several things, like when showing a keyboard, to determine available keyboards layouts. (Locales...)
  • One thing is interesting, we can find some of its informations in 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
    );
    ...
    
  • But these informations are not stored in apps preferences, unlike your score. (NSGlobalDomain, or more likely Separate domains for each of the user’s preferred languages)
  • So I would not be surprised a conflict (bug) exists in UIKit + NSUserDefaults causing that dirty plist loading loop.
  • Around 100 call you say? That's something like the number of locale/layouts in the plist!

When no keyboard is available in NSUserDefaults... (Like after synchronizing, let's imagine a bug doing this)... UIKit could try all available keyboards to determine user one, dirtily parsing this 4.4K plist hundred times... Like when showing an UIAlertView... after a NSUSerDefault sync/change.

Who knows? The Apple folks who has the source code :)

I would not be surprised going to preferences to set a keyboard other than default US then reverting to US would solve the issue. Useless in your case, but would confirm the issue. Seen that for another 4.3 bug...

As other people said, not using NSUserDefaults but a simple custom plist in /Documents could be a (in)decent workaround.

Great work on Tiny Wings! :)

Repress answered 27/5, 2011 at 23:47 Comment(3)
Delaying the calls won't fix the problem, because it would just trigger the bug during gameplay, being worse for users of older devices.Euphorbiaceous
@Euphorbiaceous quoting Andreas "i'm only calling -synchronize when the app resign active." I'm not talking about a blind 1s second perform withdelay or something like, I'm talking about waiting a significant user action like quitting game or... When he resign active like he is already doing.Repress
thanks for your suggestions. i tried all of them but none solve the problem so i just don't use the NSUserDefaults any more as you suggested and it works. Thanks!Foster
A
3

Ok, Looking around it seems that InputModeProperties.plist is just a list of hardware & software keyboards.

Looking at the forum thread you posted this issue seems to essentially be that after loading a UITextField object or UIAlertView (which includes UITextInputTraits.h amongst others) whenever you try to save user defaults there is an inexplicable loop of a keyboards definitions file. This only happens in iOS 4.3.

It seems awfully like a bug in UIKit to me and my guess is that suddenly UIKit is saving a load of things for no real purpose. If this is the case it may be hard to do anything about it although you could avoid these elements (not too bad for the alert but the textfield will be trickier) or you could switch to core data. Alternatively, you could make a mutable dictionary for all your options and save that to user defaults when the app closes and you care less about a pause. Or, just ride out an update.

Good luck (love the game btw)

Actinology answered 26/5, 2011 at 14:12 Comment(0)
R
3

EDIT

A decent bug workaround :

Short version: Just delay the bug-triggering calls until user is not annoyed.

Long version:

Since I think the issue is coming from [NSUserDefaults standardUserDefaults] call, that triggers that dirty plist-loading loop AFTER some action requesting Keyboard layouts (like an UIAlert)...

I would suggest to call [NSUserDefaults standardUserDefaults] just one time at app loading (BEFORE any bug-causing call), and keep the returned reference in a singleton class during all app lifecycle. I don't think the memory footprint would be huge... (I'm doing this in several apps w/o any issues). At worse the plist load*100 would be done only once at app load, and not during game.

If the issue comes from [userDefaults setXxxx:...] calls, same workaround, you could just keep values to save in memory and set them later in userDefaults, like just before syncing them... But at the risk of losing informations if anything goes wrong, like a crash. I personally prefer to sync after each set to ensure data integrity...

ENDOFEDIT


The short answer : iOS4.3 bug, very few chances to find a workaround... bugreport and wait for next iOS update... WWDC in 2 weeks... 1~2 month.

The long one:

After looking at UIKit asssembly, here are my guesses:

  • InputModeProperties.plist contains a list of all keyboards layouts by locale.
  • UIKit use this for several things, like when showing a keyboard, to determine available keyboards layouts. (Locales...)
  • One thing is interesting, we can find some of its informations in 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
    );
    ...
    
  • But these informations are not stored in apps preferences, unlike your score. (NSGlobalDomain, or more likely Separate domains for each of the user’s preferred languages)
  • So I would not be surprised a conflict (bug) exists in UIKit + NSUserDefaults causing that dirty plist loading loop.
  • Around 100 call you say? That's something like the number of locale/layouts in the plist!

When no keyboard is available in NSUserDefaults... (Like after synchronizing, let's imagine a bug doing this)... UIKit could try all available keyboards to determine user one, dirtily parsing this 4.4K plist hundred times... Like when showing an UIAlertView... after a NSUSerDefault sync/change.

Who knows? The Apple folks who has the source code :)

I would not be surprised going to preferences to set a keyboard other than default US then reverting to US would solve the issue. Useless in your case, but would confirm the issue. Seen that for another 4.3 bug...

As other people said, not using NSUserDefaults but a simple custom plist in /Documents could be a (in)decent workaround.

Great work on Tiny Wings! :)

Repress answered 27/5, 2011 at 23:47 Comment(3)
Delaying the calls won't fix the problem, because it would just trigger the bug during gameplay, being worse for users of older devices.Euphorbiaceous
@Euphorbiaceous quoting Andreas "i'm only calling -synchronize when the app resign active." I'm not talking about a blind 1s second perform withdelay or something like, I'm talking about waiting a significant user action like quitting game or... When he resign active like he is already doing.Repress
thanks for your suggestions. i tried all of them but none solve the problem so i just don't use the NSUserDefaults any more as you suggested and it works. Thanks!Foster
E
2

Using UIKit and OpenGL together is not recommended. I don't think that this view is the issue so much as the concept of mixing the two. I highly recommend nixing that alert and instead, show a custom overlay, so that you can accomplish two things:

  1. Make the "Get Ready" alert match the game graphics.
  2. Avoid this problem altogether.

I've seen slow performance with the currently released version on the App Store, when resuming the game on a second generation iPod touch.

If you want to keep those elements as is, this Apple Developer Forum post suggests running the synchronize on a separate thread. Going with that, I would suggest these steps:

  1. As other suggested, keep a reference to NSUserDefaults somewhere. (I usually just do something like this: #define kSettings [NSUserDefaults standardUserDefaults]. Of course, you need to invoke it once to instantiate the singleton.)

  2. Run the synchronize call on a second thread (as per that Apple Developer Forum post).

  3. See if you can call synchronize at some other time than willResignActive. The issue seems a bit worse when you call synchronize from that method.

Congrats on the game.

Euphorbiaceous answered 29/5, 2011 at 12:52 Comment(0)
C
1

Just a shot in the dark from looking at the 4.3 diff -

Perhaps a combination of the new

- (BOOL)disablesAutomaticKeyboardDismissal

on UIViewController and UIModalPresentationFormSheet (where this defaults to YES) is causing some sort of loop in your code.

Convection answered 23/5, 2011 at 10:11 Comment(0)
B
1

Assuming you execute a method to display the alert view, have you tried the following?

[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];
}

The reason I ask is that I have often experienced strange behavior when trying to execute a method straight after synchronizing NSUserDefaults. Otherwise, we'd really need to see a bit more code in order to determine what is going on.

Bartko answered 24/5, 2011 at 6:13 Comment(0)
R
1

Probably it saves each your option as separate transaction. I'm not sure. You could try to use your own singleton DataStorage class with NSMutableDictonairy as data storage. And sync it into NSUserDefaults on applicationDidEnterBackground: and applicationWillTerminate:. Or even if you're didn't use system settings with your NSUserDefaults - you could save this NSMutableDictonairy as follows:

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. thumbs up for tiny wings. ;)

Refrangible answered 26/5, 2011 at 9:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.