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.
Related
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
In my AppDelegate, I download some data from a JSON feed. I am downloading a now/next/later schedule for a few channels. I have three different view controllers for each now, next and later. In each view controller, a user can add/remove the channels so when that happens, the newly added channel data has to be downloaded again.
Since the data is downloaded in the AppDelegate and stored there, how would I pass it to the three view controllers? Should I implement three separate delegates? Keep in mind that when adding a new channel, its data has to be downloaded again (which I am doing outside the AppDelegate now).
Any help please?
Thanks
This is basically a matter of style and your approach isn't invalid as such.
I'd do it another way, though - the AppDelegate is not meant to be used as a workhorse and having several AppDelegates at the same time is simply impossible.
Here are some thoughts about how this could be done (though it's of course not the only proper way):
First, I'd employ Core Data or some other sort of storage which is available from anywhere within your app.
Then maybe I'd introduce some sort of "data controller" class (maybe a singleton). This class should handle both download of data and the distribution of that data to your viewcontrollers as requested.
By having one central controller for that purpose, you'd ensure that data gets downloaded only once because the controller knows exactly which data is already in stock.
Your viewcontrollers would neither be responsible for managing downloads anymore nor would they access the data on disk by themselves. They'd just make a request to your data controller and get a callback when the requested data is available - no matter if it was on disk already or has been downloaded for the occasion.
This keeps your VCs slim and focused and reduces the pain of making changes to your interface.
Toastor's answer is correct and as he says, there are several ways to do this. One is to call a data access class and change the values OR listen for changes on the values. For the later, the Key-Value Observing Programming Guide says the following:
KVO is particularly useful for communication between model and
controller layers in an application.
A controller object typically observes properties of model
objects, and a view object observes properties of model objects
through a controller.
In addition, however, a model object may observe other model
objects (usually to determine when a dependent value changes) or even
itself (again to determine when a dependent value changes).
Another is to make the dependency explicit, maybe passing a data access class saved in your app delegate. See Object-oriented design question, iPhone.
I'm trying to make my first application using Objective C + Core Data, but I'm not sure it's the correct way, as it feels really weird to me.
I have only one data context, which I create at launch time, in the Application Delegate. This data context is used for all the operations (read, write). In another environment (C# and LINQ for example), I try to make these operations as unitary as possible. Here it seems I just have to create the data context once, and work with it without closing it ever (except when the application exits).
I also have an asynchronous operation in which I update this data. Of course, it uses the same data context again. It works, but doesn't feel right.
My Application Delegate keeps a NSArray of the objects contained in Core Data. I use this same NSArray in all my views.
I would actually naturally close the data context once I got all the objects I require, but... aren't the objects always attached to the data context? If I close or release the data context, all these objects will get releases as well, right?
As you can notice, there is something I'm missing here :) Thanks for your help.
The NSManagedObjectContext to which you refer is more of a "scratchpad" than a database connection. Objects are created, amended, destroyed in this working area, and only persisted ("written to the database" if you prefer) when you tell the MOC to save state. You can (and should) init and release MOCs if you are working in separate threads, but the App Delegate makes a MOC available so that all code executing on the main thread can use the same context. This is both convenient, and saves you from having to ensure that multiple MOCs are kept in sync with each other.
By keeping an NSArray of Core Data objects, you are in effect duplicating its functionality. Is there any reason for not working with an NSSet of Core Data objects provided by the MOC?
If you are working asynchronously, then you should not be sharing an NSManagedObjectContext object across threads, as they are not thread-safe. Instead, create one for each thread, but set them to use same NSPersistentStoreCoordinator. This will serialise their access to the persisted data, but you'll need to use notifications to make them each aware of the others changes.
There is a good tutorial/description on how to use Core Data on multiple threads here:
http://www.duckrowing.com/2010/03/11/using-core-data-on-multiple-threads/
1) CORE DATA AND THREADS, WITHOUT THE HEADACHE
http://www.cimgf.com/2011/05/04/core-data-and-threads-without-the-headache/
2) Concurrency with Core Data
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/CoreData/Articles/cdConcurrency.html
3) Multi-Context CoreData
http://www.cocoanetics.com/2012/07/multi-context-coredata/
I'm diving into iOS programming and am using Core Data in my app to persist the game data, however, I'm wondering if my approach is the wrong way to use Core Data. I have three tables in my DB, with the first two having a one-to-many relationship with the another table (i.e. UserProfile -->> Puzzle Packs -->> Puzzles).
The approach I'm taking to use and persist the data is simple, retrieve an instance of a UserProfile using a NSFetchedResultsController and store the UserProfile object as an instance var in my App Delegate. Then I use that UserProfile object throughout the rest of the code to access and modify the state of the puzzle packs and puzzles (representing the user's progress in the game) and whenever I make a change to the objects, I just call the NSManagedObjectContext's save method to update the DB, which is is also stored in the App Delegate.
My question is, should I be fetching data from the DB anytime I need to access or modify it or is my current approach, of fetching the top object once and calling the save method often, the correct way to use Core Data?
Thanks in advance for your wisdom! I apologize if my question is odd, I'm still a noob.
As long as you're only ever working with these objects with a single NSManagedObjectContext (ie, you never have to merge changes between contexts) then I don't see a problem with fetching once, and saving as needed.
That said, the moment you work with multiple contexts (for example, using NSOperation/NSOperationQueue or other multi-threading techniques, which require separate contexts), you'll want to make sure you merge changes and update other contexts so changes from one thread don't clobber something on another.
I'm working on a Core Data iPhone app that pulls remote resources from the web into NSManagedObjects and saves them locally.
I want the user to be able to designate which of these objects should be saved. This means that some will be saved, but many should be deleted. However, I might want to save and delete at different times - I'd prefer to save designated objects immediately (in case the app crashes), but still keep around the other objects because they're hanging out in table views and such.
One approach I can think of is to have a different persistent store - one for stuff that will be saved, one for stuff that won't; this way I can save the "should be saved" store at any time. However, I would much prefer to keep objects of the same type in the same domain.
Another approach would be to just save at the very end - negating any ability to recover from a crash. But saving at the end would allow me to parse out any objects that weren't designated "should save".
And that's really what I want - a "shouldSave" method in the NSManagedObject class, or at least a save method that I could fire at select objects. But as far as I can tell, neither of those exist.
So, if anyone has any other suggestions, please let me know! It would be greatly appreciated.
CoreData is not for object serialization, it is an object graph serialization. That is an important distinction. Once you have an NSManagedObject it is associated with a context, and CoreData handles saves at context level since that is the only way it guarantee any sort of object graph consistency. In other words, you can't save individual objects because if they have relationships with other objects you would need to also save those objects and it quickly cascades out to the whole graph.
You seem to be worried about crash recovery. If the app crashed and the user relaunched it would they expect to see just the items they saved, or everything that was on the screen before they crashed? If it is the former you should just delete them at save time and remove them from the users view (with some animation), if it is the later you should commit out everything, and potentially delete the objects you are not interested in at another time.