How to cache NSFetchRequest results without NSFetchedResultsController? - iphone

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.

Related

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

Core Data with only one Data Context. Is it right?

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/

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.

Persisting data objects on the iphone

I was wondering what your thoughts were on persisting objects (NSDictionarys and NSMutableArrays of Objects) on the iPhone?
I've gone through a couple of options in my head:
1.) We persist using NSUserDefaults, this method seems the easiest and I sort of want to just go ahead and use this. The Data I want to store would only maybe be a few kilobytes of objects.
2.) Using NSKeyedArchiver (the exact name alludes me atm) to archive/serialize and then just puting that into a SQLITe database. but the problem with that is:
I need the data, when it's changed on the phone, to automatically be saved. NSUserDefaults has a synchronization method for this, but for method #2 I'd have to re-archive the entire array of objects, and then store it back into the database. This seems like a lot of work.
3.) forgot about this I've heard things about CoreData being able to handle this sort of thing, but I'm not sure if I understand exactly.
So what do you guys think? Do you have any other suggestions?
I recommend going the extra mile and use Core Data.
First you will learn a new thing, a good thing.
But further more your app will start up faster and handle data much more fluently as data is fetched in chunks rather requiring reading the whole dataset.
...don't be afraid, just try it. Start small and keep going.
I think NSDictionary and NSArray has writeToFile:atomically: instance methods that serializes and writes them to your given filename. You could add the observer for your NSDictionary or NSArray which writes them after they get changed.
I've used both NSKeyedArchiver and writeToFile:atomically: on a few of my own apps with great success.
Performance has been great. You can repeatedly persist a few thousand lines of text and not notice.
Personally, I avoid sqlite unless you need querying. You'd be surprised what you can get away with using only NSDictionary filled with NSArray
The advantage to using NSKeyedArchiver is once you start creating "Domain Objects", you can persist those the same way. For this reason, I use NSKeyedArchiver 99% of the time.

Good strategies for REST -> XML -> Core Data -> UITableView?

What are good practices for asynchronously pulling large amounts of XML from a RESTful service into a Core Data store, and from this store, populating a UITableView on the fly?
I'm thinking of using libxml2's xmlParseChunk() function to parse chunks of incoming XML and translate a node and its children into the relevant managed objects, as nodes come in.
At the same time that these XML nodes are turned into managed objects, I want to generate UITableView rows, in turn. Say, 50 rows at a time. Is this realistic?
In your experience, what do you do to accomplish this task, to maintain performance and handle, potentially, thousands of rows? Are there different, simpler approaches that work as well or better?
Sure, this is a pretty standard thing. The easiest solution is to do the loading in a background thread on one MOC, and have the UI running on the main thread with its own MOC. Whenever you get a chunk of data you want to have appear (say 50 entries), you have the background MOCsave:.
Assuming you have the foreground MOC rigged to merge changes (via mergeChangesFromContextDidSaveNotification:) then whenever you save the background MOC the foreground MOC will get all of those changes. Assuming you are using NSFetchedResultsController it has delegate methods to cope with changes in its MOC, and if you are using Apple's sample code then you probably already have everything setup correctly.
In general CoreData is going to be faster than anything you roll yourself unless you really know what you are doing and are willing to spend a ton of time tuning for your specific case. The biggest thing you can do is make sure that slow things (like XML processing and synchronous flash I/O caused by save:) are not on the main thread blocking user interaction.
Joe Hewitt (Facebook app developer) has release much of his code as open-source. It is called Three20. There is a class there that is great for fetching internet data and populating it into a table, without the need for the data beforehand. The classes used for this are called TTTableViewController and TTTableViewDataSource.
From here, it would not be much of a stretch to store as CoreData, just subclass the classes as you see fit with the supplied hooks.
If you are worried about too much data, 50 at a time does sound reasonable. These classes have a built in "More" button to help you out.
From the Three20 readme:
Internet-aware table view controllers
TTTableViewController and
TTTableViewDataSource help you to
build tables which load their content
from the Internet. Rather than just
assuming you have all the data ready
to go, like UITableView does by
default, TTTableViewController lets
you communicate when your data is
loading, and when there is an error or
nothing to display. It also helps you
to add a "More" button to load the
next page of data, and optionally
supports reloading the data by shaking
the device.
No one has mentioned RestKit yet? My friends ... seriously, you have to check this out. If you are doing anything with REST on iOS (and now on OS X) and particularly if you're wanting to work with Core Data ... PLEASE have a look at RestKit. I've saved countless hours implementing some pretty complex data synchronization between a server and my Core Data models on iOS. RestKit made it so damned easy, it almost makes you sick.