NSFetchedResultsController does anyone have any insights into the cache implementation? - iphone

this is a bit of an odd question, so I'll start at the beginning...
My needs for NSFetchedResultsController (NSFRC) are the ability to perform filtering and sorting after the objects have been fetched, mostly because the filtering and sorting require querying the fetched objects themselves, and is therefore not possible with NSFRC. So, I wrote my own class, BSFetchedResultsController, which aims to replicate the functionality of NSFRC (delegate notifications, automatic sectioning and caching) but with added hooks for the user to set their own blocks for filtering and sorting. The code for this class is on github here if anyone wants it: https://github.com/blindingskies/BSFetchedResultsController, although I wouldn't consider the class ready yet as a drop in replacement of NSFRC.
So, I've not yet implementing caching, mostly because I'm not really sure how Apple has implemented it. The caches are stored in binary files here:
{app dir}/Library/Caches/.CoreDataCaches/SectionInfoCaches/{cache name}/sectionInfo
So, presumably, my class would need to store its caches in a similar location? How is this structure organised/work? The cache needs to store the NSFetchPredicate (or properties required to re-generate it), and it needs to archive the fetched objects somehow. But, NSManagedObject doesn't conform to NSCoding, so, how does it archive the objects? And lastly during the NSNotificationCenterDidChangeNotification handler the cache needs to be updated.
So, the real aspect of this is how to archive the fetched objects, I'm leaning towards just saving the objectIDs in an array? And then just get those objects from the context. Is that enough?
If anyone has thought about how to implement

Okay, so to answer my own question, I've implemented the cache as follows:
Created another class which retains the entity (NSEntityDescription), fetch predicate (NSPredicate) and sort descriptors (NSArray) of the NSFetchPredicate, along with the sectionNameKeyPath and additional BSFetchedResultsController objects (post fetch predicate, filter, comparator). Make this class NSCoding compliant.
Then at the start of the performFetch: method, if there is a cache name, unarchive the object and see if the properties match the BSFRC, and if it does, then use the cache's section data.
Then add another notification handler, to NSManagedObjectContextDidSaveNotification to flush the objects to the cache.
A couple of points... I found that archiving the NSFetchRequest directly (which is NSCoding compliant) didn't work, and at the moment, am only checking the name of the NSEntityDescription.
Also, I don't cache the whole object graph, just the URIRepresentation of the NSManagedObject's NSManangedObjectIDs. Then, I respawn these URIs given the managed object context after validating the cache.
It seems to work, although I'm not sure how often I should flush the objects to the cache...

Related

Core Data Fetch

I have an entity, and I want to fetch a certain attribute.
For example,
Let's say I have an entity called Food, with multiple attributes. I want to select all categories, which is an attribute on each food item. What's the best way to accomplish this in Core Data?
Just run your fetch request and then use valueForKey: to extract all of the attribute values. If your model contains lots of objects, you can set the fetch limit and offset (and sort descriptor) to page through the items. When doing this you should also set the fetch request to not return objects as faults.
Just remembered there is an alternative. You can set the properties to fetch and then set the result type to NSDictionaryResultType. You still need to do the iteration but this will return the minimum data possible.
EDIT: I think I misunderstood your question. It seems that you only want to fetch a property of an object, not the object itself (e.g. the attribute but not the entity)? I don't believe core data is going to work that way...it's an object graph rather than a database, as the person above mentioned. Research how Core Data "faults", automatically retrieving dependent objects as they are needed. I left the advice below in case it still applies, though I'm not sure it will.
You can add a predicate to your search in order to fetch only objects which meet certain criteria; it works like an "if" statement for fetching. Here's Apple's documentation:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSPredicate_Class/Reference/NSPredicate.html
And a tutorial:
http://www.peterfriese.de/using-nspredicate-to-filter-data/
All that said, the necessity really depends on how many objects you're fetching. If it's not causing any performance hit, there's not necessarily anything wrong with fetching a few un-needed objects. Solve problems, in other words--don't "optimize" things that were working fine. If your model contains a ton of objects, though, it could be expensive to fetch them all and you would want to use a predicate.
You can only fetch whole objects, but you can only fetch objects that have a perticlar attribute using nspredicate in your fetch request. See the code snippet in Xcode in the snippet section. Look for fetch request in the find field and drag out the snippet with the nspredicate code. You can set the predicate to only find the objects that satisfy this predicate. Hope this helps!

Alternative to NSFetchedResultsController?

Currently I have a UITableView with its data source being an NSFetchedResultsController. The most important thing the NSFetchedResultsController does is automatically update my table if there are any changes, via delegate methods. However, I no longer need to do a fetch to get my entity, call it "Pictures" for now. I have another entity called Folder, and folders have a relationship with Pictures, so every folder has an NSSet pictures.
So instead of fetching all pictures that belong to a certain folder, now I can just do folder.pictures, and that returns what I need, and I can assign that to an array and set that as my tableView source. However, this doesn't give me automatic table updates like an NSFetchedResultsController would.
My question is how can I have the functionality of an NSFetchedResultsController (that is, the delegate methods that automatically update my table) without executing a fetch? I don't need to fetch anymore since I have an NSSet with the desired NSManagedObjects.
What's wrong with the fetched results controller? Just keep it and use the dot notation for relationship sets as well - you get the best of both worlds.
The real advantage of the fetched results controller is actually hidden. It will fetch your objects (folders) alright - but maybe it will not fetch all the relationship attributes (pictures). This is called faulting. It means that core data will get the data in the background if it is needed. It is automatically optimized for speed and good memory usage. For example, the potentially huge array of your datasource will not have to be all in memory at once, something that is unavoidable with an array.
Thus, you really do not want to get rid of the FRC. She is your friend. Stay faithful to her. ;-)

Is it necessary to cache data returned from CoreData?

I store data in an iPhone app using CoreData. The data stored is intended to represent a tree, with each record being a node (with pointer to its parent). As the user navigates through this tree, I cache the current parent and current set of children in an NSArray. I initially thought this made sense because it could allow quicker access of sibling nodes (useful when the data is presented in a UITableView and the user scrolls). I felt that using CoreData to perform a "get all children" query each time the next child is required would be inefficient (i.e. I'd be making the same query n times for n children, and just returning the ith result each time).
Now, I am not so sure this is necessary nor smart (mostly because I've learned that CoreData is a beast I have yet to fully understand).
Does anybody know whether this is good practice? I know CoreData does a lot for free including the caching of results, but since I know my data is going to be used like a tree, would my implementation (or some other outside-of-CoreData cache implementation) be more efficient?
when you use coredata and an UITableView you should use a NSFetchedResultsController. It does all the caching for you, and for sure it does it better than you.
the fetchedResultsController also takes care of deletion, changing and insertion of coredata objects, ie it tells you when to update the tableview. but for the latter to function you have to set up the fetchedresultscontroller delegate methods. there is a complete example of the delegate functions included in the documentation.
in general, get rid of your caching, it's not needed.

NSKeyedArchiver encode only part of an array

I have a list of objects that can sometimes change, and I want to keep a persistent cache on the device whenever the app is closed or move to the background.
Most of the objects in the list will not change, so i was wondering what is the best way to save the list. I have two major options i think about:
Using NSKeyedArchiver / unArchiver - This is the most convenient method, because the objects i'm serializing hold other custom objects, so this way i can just write a custom encode method for each of them. The major problem is that i didn't find on Google how to serialize only the changed objects, and serializing the entire list every time seems very wasteful.
Using SQLite - this is what i'm currently using, and the worst problem here is that adding \ changing properties of the objects is very complicated, and much less elegant.
Is there any way that i can enjoy the convenience of NSKeyedArchiver but only serialize the changed objects?
Like Adam Ko I would suggest using Core Data:
This kind of problem is what it's really good at, after all!
If your cache items are independent from each other, this could be achieved by simply wrapping your cache-items by a thin layer of NSManagedObject (i.e. you could benefit from Core Data with only minor changes to your app).
This wrapper entity could store an archived version of a cache item in an attribute of type NSBinaryDataAttributeType and provide access to the unarchived object through a transient property.
See Non-Standard Persistent Attributes for an example.

Create a Core Date Entity Instance But not want it to be stored(non-persistent)

Sometimes I need instantiate CoreDateEntity to store some infomations for temporarily using.
But I needn't it be stored into DB.
currently I created a similar class which have same structures as the CoreDateEntity does.
It works well but I have to do many datas transfer between Two models.
Is there any better way to handle this?
Thanks for all the replies. but you guys just give me half answer of this. consider about this, I need place some entity without MOC into current database pool, how could I do this? I already checked the documents of CoreData, seems I didn't find API to transfer one entity from MOC to another MOC(manage object context).
According to Apple docs you can initialize a managed object without context if you specify nil as context.
- (id)initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context
You can assign entities to different stores when you set up the data model. Have one store be the persistent store and the other an in-memory store. You can't form relationships across stores but it sounds like you don't need that.
To assign a configuration, hit the configuration tab (the one with the wrench icon) in the entity detail (where you give it its name, class and parent). When you create the persistent store, add the configuration name to the options dictionary.
Update:
I think you maybe overcomplicating things. It sounds like you have some managed objects that will be temporary and some that will persisted but sometimes you may want to save the temporary objects. I don't think you should bother trying to separate out the "temporary" objects. Doing so just adds complexity without any performance benefit. Instead, just use ordinary persisted objects and then delete the ones you don't want.
Always try the simplest solution first.
Use two different managed object context's and only save the objects from one context. Be careful not to set relationships between objects of two different context's - this doesn't work.