What are the best practices to cache the data? - iphone

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

Related

Framework for caching files using NSCoding

I want to save / load instances of my classes using NSCoding. Each class should have an individual cache time / time to live. When the app starts up I want the instance of my class to be either loaded from cache if its not expired or otherwise created normally.
This problem must have been solved hundreds of times....
Is there a good framework for doing this, or should I roll my own?
maybe you can try NSCache NSCache doesn't conform to the NSCoding protocol so you can't archive / unarchive the cache.
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.
To be clear, you can save or loading objects, not classes, using NSCoding.
I'm not aware of a framework that does this. You can either use the file modification time as the cache time or you can keep it in a separate database. If it's not too many different classes, you can use NSUserDefaults as that separate database. Each class would just be coded to know its own time to live.

Advantages of using NSUserDefaults to save game settings/state?

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.

How to cache NSFetchRequest results without NSFetchedResultsController?

Is there a way to cache NSFetchRequest results, but without using an NSFetchedResultsController? I know how to set the cache name for the results controller, but in a number of instances I just pull data from my core data store and want to cache it -- but have no need for a results controller. Any ideas on how to do this? I can't find anything in Apple's documentation.
You can keep it in memory in an NSArray. If you want to cache between executions of your application you can't. That portion of the NSFetchedResultsController is not published and the internal structure of Core Data is not open.
If a cache is absolutely needed then use a NSFetchedResultsController.
However, considering the speed and performance of Core Data, I would question the absolute need for a cache. I suspect performance can be gained in other areas.

How to save objects using Multi-Threading in Core Data?

I'm getting some data from the web service and saving it in the core data. This workflow looks like this:
get xml feed
go over every item in that feed, create a new ManagedObject for every feed item
download some big binary data for every item and save it into ManagedObject
call [managedObjectContext save:]
Now, the problem is of course the performance - everything runs on the main thread. I'd like to re-factor as much as possible to another thread, but I'm not sure where I should start. Is it OK to put everything (1-4) to the separate thread?
Yes, I recommend reviewing both Apple's Docs on multi-threaded Core Data and my article on the MDN (Mac Developer Network) http://www.mac-developer-network.com/columns/coredata/may2009/ which discuss the things you need to avoid and how to set everything up.
BTW, saving a lot of binary data into a Core Data object is generally a bad idea. The rule goes:
< 100KB save in the entity
< 1MB save in a separate entity hanging off a relationship
1MB save to disk and store its path into the managed object
Therefore you could spin off the download of the binary data into separate threads, save them to disk and then tell the main thread the NSManagedObjectID of the referencing object and the path and let the main thread do the very quick and easy linking. That would let your Core Data implementation stay single threaded and only spin off the data downloads.

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/