Advantages of using NSUserDefaults to save game settings/state? - iphone

Is there any benefit in going with NSUserDefaults to save game state/settings over creating a binary file in plain C?
Specifically for an iOS game.

The biggest advantage with NSUserDefaults is that you don't have to worry about the actual writing to "disk" — -synchronize is automatically called at "appropriate times", which includes app background (if multitasking is supported) or termination (if multitasking is not supported, or you set UIApplicationExitsOnSuspend or whatever it's called). This means you can update NSUserDefaults much more frequently without as much overhead.
The disadvantage is that I'm pretty sure the whole thing is loaded into memory and a write involves writing the whole file (I think it even writes to a temporary file and then does a rename), so it's not suitable for storing large, infrequently-changing blobs (e.g. photos).
Unless your saved games are huge, I'd do something reasinably simple with NSUserDefaults, mostly because it avoids you having to keep track of a bunch of files.
EDIT: A potentially major drawback with NSUserDefaults is that doesn't save when the app crashes, so you might still want to explicitly -synchronize occasionally, depending on how crashy your app is. This is particularly noticable for developers, since pressing "stop" in Xcode (or unplugging the phone while debugging) is effectively a crash!

NSUserDefaults is the appropriate location for settings, but probably not save games.
The advantage is you get in-memory caching and several decades of stability behind it. The downside is you are fairly limited in what you can put in a user defaults file (objects must be plist-friendly, i.e. strings, numbers, data, dates, dictionaries, and arrays only.) As well, saving game state in NSUserDefaults would make it more difficult to handle multiple save games.
I would recommend you build your logic classes such that they can be serialized/deserialized to/from an arbitrary byte stream (easiest way to do that is to implement NSCoding on your various game state classes.) Then you can just dump files to disk to arbitrary locations (e.g. a "quicksave" copy in NSUserDefaults and a list of full savegames in [yourapp]/Documents/. The task of encoding/decoding is not actually strongly associated with the task of storage/retrieval, as surprising as that might be.

Related

iPhone - keep objects in permanent memory options

I need to save my objects in permanent memory. The option I use right now is that i save my objects in the NSUserDefaults before my app quits and I retrieve them when my app starts running. This approach is not very convenient since I may lose important data in case the application crashes. Is there any way to store my objects, but when a property of an object changes, then this change is saved in the disk automatically? Except for that, there is a danger to mess the objects in ram and the objects in the disk using that architecture.
For example
-> Load objects from memory
-> [object1 setValue:#"5"]
-> Application crashes
After the crash, when the user opens the application, the value #"5" will not be available because I never saved the data.
Is there any alternative so as to make by code more safe and maintainable? CoreData is a good option for this problem , or is it an overhead?
If you need to save data, CoreData is the best. You can save after every change if you need to.
If saving info is so important you might want to figure out what could cause/is causing your application to crash and fix that. No matter how good a solution is if your app is going to randomly crash you'll probably lose some data.
It is really not the best of option to save data instantly when a value changes. It's way too expensive for app performance. You must continue using NSUserDefaults to store the values when app is force closed or entered in background.
Your app is designed by you. It MUST not crash but even if it does, don't worry. Anamolies cannot really be handled.

What are the best practices to cache the data?

What are the best practices to cache the data in iOS apps connected to data source via web service?
You should lookat NSCache
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSCache_Class/Reference/Reference.html
An NSCache object is a collection-like container, or cache, that
stores key-value pairs, similar to the NSDictionary class. Developers
often incorporate caches to temporarily store objects with transient
data that are expensive to create. Reusing these objects can provide
performance benefits, because their values do not have to be
recalculated. However, the objects are not critical to the application
and can be discarded if memory is tight. If discarded, their values
will have to be recomputed again when needed.
Depends on the type of data
for binary data (files):
- Cache your files in the Cache folder using NSFileManager and NSData writeToFile:
for small ammounts of data (ascii/utf8):
- Use NSUserDefaults
for large ammounts of data (ascii/utf8):
- Use a sqlite3 database
It depends on how much data you want to cache and how you'll be accessing it once you have it cached, and a bunch of other cache management issues.
If you have a small amount of data, you could store that in a dictionary or array, and simply write it out and read it in. But this kind of solution can become slow if you have a lot of data; those reads and writes can take a long time. And flushing a dirty cache to disk means writing the whole object.
You could write individual files, but again, if you have a lot of files that might become a performance issue as well.
Another alternative is to use CoreData. If you have a lot of data (say, many objects) it may make sense to define what those look like as CoreData entities. Then you just store and fetch objects as you need them, falling back to fetching from your web service (and then caching) if the data is not local. You can also optimize other cache management tasks (like expiring unused entries) easily and efficiently using CoreData.
I actually went down this road, with a couple different apps. I started with an NSDictionary, and that became quite slow. I switched to CoreData, which not only simplified a lot of my code for cache initialization and management, but gave the apps quite a performance boost in the process.
If you're using NSURLConnection, or anything that uses NSURLRequest, caching is already taken care of for you:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html#//apple_ref/doc/uid/20001836-169425
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/Concepts/CachePolicies.html#//apple_ref/doc/uid/20001843-BAJEAIEE
By default these use the cache policies of the protocol, which for a web service would be the HTTP headers it returns. This is also true, IIRC, of ASIHttpRequest.
Core Data also implements its own row and object caching, which works pretty well. So the reality here is that you really don't need to worry about caching when it comes to these things - it's optimizing your use of things like NSDateFormatter that starts to become important (they're expensive to create, not thread safe, etc...)
And when in doubt, use Instruments to find bottlenecks and latency

Is dynamic code insertion possible in iphone?

Each time i make small changes, i need to submit the app for approval and need to wait to review process to complete, so till what extent i can exploit dynamic code insertion or at least can i change the view of a view controller using the nib file which i will be downloading to document directory dynamically?
For the most part, the answer is no, and intentionally so. In particular, you cannot run executable code that you download. If you figured out a workaround to allow it technically (and I have at least one or two thoughts in mind on how I'd attack it), it would definitely run counter to Apple's approval process.
On the other hand, a nib file is data, not code. That you can definitely read from disk and instantiate using [UINib nibWithData:bundle:]. This is a pretty big hassle, though, and I don't particularly recommend it. But it's relatively straightforward (unless you want to correctly handle low memory situations, in which case it's a bit more of a hassle).
Of course you can always have dynamic code by putting it in a webview and writing it in JavaScript.
In the end, though, the review process is the way it is on purpose, and I recommend developers adapt their development cycle to include it. That generally means having fewer releases with heavier testing because "one quick fix" is expensive.

Best Practice for NSUserDefaults synchronize

I'm using [[NSUserDefaults standardUserDefaults] synchronize] each time I write anything to the plist. Is that overkill? Or are there adverse effects in doing this?
Yes it may be overkill but in a simple application will you notice a performance hit? probably not if you are only saving after basic user interaction such as the user selecting their settings. The benefit to calling synchronize more often is if your application may crash and the information you are saving is important, otherwise iOS will save it for you periodically.
The synchronize method, which is automatically invoked at periodic intervals, keeps the in-memory cache in sync with a user’s defaults database.
Calling it frequently may cause performance issues, but it's not an overkill if it's a small application (like already mentioned) OR if you really need your plist to be up to date with the changes made in the current thread or changes made in some other thread in the application.
Because this method is automatically invoked at periodic intervals, use this method only if you cannot wait for the automatic synchronization (for example, if your application is about to exit) or if you want to update the user defaults to what is on disk even though you have not made any changes
Probably the only adverse effect you might notice is a negligible decrease in performance.

NSXMLParser iPhone memory strategy for large xml

I build a parsing algorithm using NSXMLParser.
Im having doubt as to what is the best strategy for keeping my memory usage on a minimum.
I have a valueObject (e.g. "Person") this object has ≈ 30 NSString properties, while parsing the xml I continually alloc and release a temporary Person object as the nodes are traversed.
I checked this and there is only one of these Person objects instantiated at any time.
When a node is traversed and a Person is "build" I pass the Person to a NSMutableArray and release this Person. Seems no problem there. (I'll need the array for a tableView).
When I reach around 50+ Person objects in the array my app just quits, didReceiveMemoryWarning doesn't get called, no other warnings, no parseErrorOccurred, nothing?
If I limit the number of Persons in xml the app does just fine, I haven't been able to find any memory leaks with Instruments.
I think that I simply can't hold 50+ Person objects in an array… seems a bit harsh, but I haven't got much memory experience with the iPhone, so this is just a guess.
The xml is search results from which the user probably only needs a few, so persisting them to my core model to keep them around for display seems a bit crazy.
What would be a good strategy for keeping these Person objects around? or am I missing a huge memory leak since the iPhone should be able to handle much more than this?
Hope some experienced developers can point me in the right direction:)
Thank you!
Despite NSXMLParser being a SAX-based parser it does not support parsing an input stream, which means that the entire XML string you are parsing is kept in memory. This on its own is a big issue, but as you parse the problem gets worse as you start duplicating the string data from the XML in your Person objects.
If your strings are really big, you've got the second problem of having too many parsed Person objects in memory at one time.
The first problem can be solved by using AQXMLParser from Jim Dovey's AQToolkit library, which provides an NSXMLParser-like API but with support for streaming the data from disk.
The second problem can be solved using a disk-based persistence technology, like Core Data, SQLite Persistent Objects, or even just storing the Person objects on disk yourself.
How long are those strings? Generally, on the iPhone 3G and older models, your app should have a minimum of about 20 MB of memory available (much more on the 3Gs). This is no absolute rule, of course, but a decent rule of thumb. To occupy this much memory with 50 objects would mean ~400-500 KB per Person object. Is this in the ballpark? If so, you will probably need a memory management strategy that does not keep all objects in memory at the same time. Core Data can probably help you a great deal in that case.
If you did not receive a memory warning it is probably not the reason your app is quitting. In Xcode go to the organizer, and select the device, then click on the console tab. If they app was shutdown for memory reasons there will be a system message in the console log saying it is killing the app due to memory pressure.
The answer is to chop up the incoming stream, I wrote a post about it some time ago:
https://lukassen.wordpress.com/2010/01/15/feeding-nsxmlparser-a-stream-of-xml/