I wonder what the guidelines are for:
1 - how often I can read from NSUserDefaults
2 - how much data I can reasonably store in NSUserDefaults
Obviously, there are limits to how much NSUserDefaults can be used but I have trouble determining what's reasonable and what isn't.
Some examples among others:
If my game has an option for the computer to be one of the players, I will use NSUserDefaults to save that boolean value. That much is clear. But is it also reasonable to access NSUserDefaults during my game every time I want to know whether the computer is a player or should I be using an instance variable for that instead? Assume here I need to check that boolean every second. Is the answer the same is it's 100 ms instead? What about every 10 s?
If my game has 50 moving objects and I want their positions and speeds to be stored when the user quits the app, is NSUserDefaults a reasonable place to store that data? What about 20 moving objects? What about 200?
Don't worry about limits. Instead, ask yourself this simple question:
Is this a preference?
If it is a preference, then it should be in user defaults. That's what user defaults is for. If not, then it should be in the Documents directory (or, on the Mac, possibly in Application Support).
On iOS, you might tell whether it's a preference or not by whether it would be appropriate to (if possible) put it in your settings bundle for display and editing in the Settings application. On Mac OS X, you can usually tell whether it's a preference or not by whether it would be appropriate to put it in the Preferences window.
Of course, that relies on your judgment. Stanza for Mac, for example, gets it wrong, putting non-preferences in its Preferences window.
You can also consider the question by its converse:
Is this user-created data?
A preference that you will have a default value for is not user-created data; it is user-overridden data. No less bad to lose it, but it informs where you should keep it.
I wonder what the guidelines are for:
1 - how often I can read from NSUserDefaults
quite regularly. expect the defaults' overhead to be similar to a thread-safe NSDictionary
2 - how much data I can reasonably store in NSUserDefaults
physically, more than you'll need it to. the logical maximum is how fast you need it to be, and how much space it takes on disk. also remember that this representation is read written to/from disk at startup/shut down and various other times.
If my game has an option for the computer to be one of the players, I will use NSUserDefaults to save that boolean value. That much is clear. But is it also reasonable to access NSUserDefaults during my game every time I want to know whether the computer is a player or should I be using an instance variable for that instead?
just add a const bool
to the opponent object. zero runtime loss, apart from the memory, which will not be significant.
Assume here I need to check that boolean every second. Is the answer the same is it's 100 ms instead? What about every 10 s?
again, it's like a thread-safe NSDictionary (hashing). it will be fairly fast, and fast enough for reading at that frequency. whether it's the best design or not depends on the program. if it becomes huge, then yes the performance will suffer.
If my game has 50 moving objects and I want their positions and speeds to be stored when the user quits the app, is NSUserDefaults a reasonable place to store that data? What about 20 moving objects? What about 200?
it would be fine, although i would not read/write via user defaults during game-play; just save/load the state as needed.
i don't recommend saving all this in user defaults. just create a file representation for your game's state and use user defaults for what it's designed for. if it's huge and you write to it often, then the implementation may flush the state to disk regularly, which could take a relatively long time.
The main performance issue no-one here is mentioning is that the user’s home directory may be on a network volume, and may not be particularly fast. It’s not an ideal situation, but it happens, so if you’re worried about performance that’s what you should be testing against.
That said, NSUserDefaults
uses an in-memory cache, and the cost is only incurred when synchronizing. According to the documentation, synchronization happens “automatically … at periodic intervals”; I believe this only applies if something has changed, though.
So, for the case of checking whether the computer is a player, using NSUserDefaults
once a frame shouldn’t be a problem since it’s cached. For storing game state, it may be a performance problem if you updated it constantly, and as Peter Hosey says it’s a semantic abuse.
Hundreds to thousands of items are fine in NSUserDefaults (it's basically just a wrapper around property list serialization). With respect to the overhead for your application, the best thing to do is try it and use a profiler.
NSUserDefaults is basically a wrapper for loading a NSDictionary from a .plist file from the disk (and also writing it to the disk). You can store as much data in NSUserDefaults, but you have little control over how much memory it uses, and how it reads from the disk.
I would use different technologies for different information/data.
Small bits of data from servers, preferences, user info, et cetera, I would use NSUserDefaults.
For login information (access tokens, sensitive data), I would use the keychain. The keychain could also be used for data that should not be deleted when the app is deleted.
For large amounts of server data or game data, I would write it to the disk, but keep it in memory.
In your situation, I would keep it in memory (probably a @property), but I would periodically write it to the disk (perhaps every 1 to 5 times it changes, use an int ivar). Make sure that this disk writing method is in the AppDelegate, so that it won't fail when you close the view controller that is executing it.
This way, the data is easily accessed, but its also saved to the disk for safe keeping.