Alternative to NSFetchedResultsController? - iphone

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. ;-)

Related

Can I remove an object from the fetchedResultsController without deleting the object from the db?

I have been trying to remove an object from the tableView I am working with without deleting the object from the db, and I can't figure out how to do it.
I have tried setting a predicate on the fetchedResultsController, to filter objects that have a BOOL set to a certain value. Then, when I change that value, I expect that I can get that object to stay in the db, but out of the fetchedResultsController because of the predicate, but alas, that isn't working.
How can I remove an object from my tableView's dataSource (the fetchedResultsController) without deleting it from the core data db completely?
Please help! I've been bashing my head against this for way too long
I've always rolled my own table view data source when I need to weed out fetched results. NSFetchedResultsController objects are great when you want to show everything in your data base, but not so great when you want to weed out some of the data on the fly. I fetch the data, then iterate through the results array looking for the data I want to keep. The good objects get added to a new array, which becomes the basis of my table view data source.
The times I've implemented this, the data manipulation happens in a model object which hands off the array to the UITableViewController subclass that implements UITabelViewDataSource protocol methods. I suppose you could implement this as a subclass of NSFetchedResultsController, but I've never tried that approach.

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.

Using NSFetchedResultsController Without UITableView

Is it wrong to use an NSFetchedResultsController purely for data management, i.e., without using it to feed a UITableView?
I have a to-many relationship in a Core Data iPhone app. Whenever data in that relationship changes, I need to perform a calculation which requires that data to be sorted. In Apple's standard Department/Employees example, this would be like determining the median salary in a given Department. Whenever an Employee is added to or removed from that Department, or an Employee's salary changes, the median calculation would need to be performed again.
Keeping data sorted and current and getting notifications when it changes sounds like a great job for NSFetchedResultsController. The only "problem" is that I'm not using a UITableView. In other words, I'm not displaying sorted Employees in a UITableView. I just want an up-to-date sorted array of Employees so I can analyze them behind the scenes. (And, of course, I don't want to write a bunch of code that duplicates much of NSFetchedResultsController.)
Is it a bad idea to use an NSFetchedResultsController purely for data management, i.e., without using it to feed a UITableView? I haven't seen this done anywhere, and thought I might be missing something.
I would not call it bad but definitely "heavy".
It would be less memory and CPU to watch for saves via the NSManagedObjectContextDidSaveNotification and do the calculation there. The notification will come with three NSArray instances in its userInfo and you can then use a simple NSPredicate against those arrays to see if any employee that you care about has changed and respond.
This is part of what the NSFetchedResultsController does under the covers. However you would be avoiding the other portions of the NSFetchedResultsController that you don't care about or need.
Heavy
NSFetchedResultsController does more processing than just watch for saved objects. It handles deltas, makes calls to its delegates, etc. I am not saying it is bad in any way shape or form. What I am saying is that if you only care about when objects have changed in your relationship, you can do it pretty easily by just watching for the notifications.
Memory
In addition, there is no reason to retain anything since you are already holding onto the "Department" entity and therefore access its relationships. Holding onto the child objects "just in case" is a waste of memory. Let Core Data manage the memory, that is part of the reason for using it.
There's nothing wrong with using NSFetchedResultsController without a view. Your use case sounds like a good reason to not re-invent the wheel.
To me, this sounds like an appropriate use of NSFetchedResultController. it might be a bit overkill, as its primary use IS to help populate and keep up to date tableViews, but if you are willing to put up with the added complexity, there is no reason to not use it as such. Correct use of notifications would be the other method and it is just as complex i would estimate.

NSManagedObject for temporary use, how to switch between NSObject and NSManagedObject

Im using a Core Data model for my iPhone app. I have been looking for a way to instantiate or use an Entity outside the ManagedObjectContext. (This should not be done, I know, Im also more looking for a way to not do that, but get the benefits anyway).
My challenge is that I have a view where the user can search for "Persons", all the search results are parsed and put into a Person managedObject then displayed in a list.
If the user clicks a Person from the list, then and only then would I like the Person entity to be persisted to the store, however this requires me to delete all the other results so they don't get persisted along with the desired one. Also to the best of my knowledge, if the user decides to quite the app, the store is persisted, potentially with all current search results mixed in with real user data!
Is there some way I could have a TempPerson NSObject I could use for the search list? Without, however, me having to manually pull the 45 attributes from the temp object and manually set them on the managedObject!
Sort of like:
NSManagedObject aPersonCorrectlyReturnedFromTheStore = (NSManagedObject *)tempPersonOfJustTypeNSObject
I have seen example code from Apple where they build a temporary store to facilitate undo/redo and other stuff on an object that is not yet persisted. This I feel would be overkill in my situation. I just need to display search results until the user selects a Person to persist.
Hope it is clear what Im trying to do, feeling like my Core Data vocabulary isn't quite large enough yet:)
Thanks for any suggestions.
You could create each temporary person object as an NSDictionary or NSMutableDictionary. You can then create a new Person managed object and use the fact that NSManagedObject instances are KVC compliant and use setValuesForKeysWithDictionary:.
New managed objects that are inserted are not actually persisted until you send the managed object context a save: message.
Keep track of them in a collection (set or array) -- you are probably already doing this since you are presenting the search results somehow. Then, delete (deleteObject:) them all except for the one(s) that the user selects.
The deleted managed objects will never be stored.

Faulting a CoreData relationship when fetching the main entity

I have an entity with a number of to-many relationships. I present certain properties of the entity in a tableview, using a NSFetchedResultsController. Of all the relationships the entity has, the values of only 1 of the relationships are displayed (they are currently faulted in the cellforrowat... method). It seems to me that this could have a performance impact. Is it possible to fault a specific relationship at the time of creating the Fetch request, so that CoreData does not have to fetch the values when the table is being scrolled?
I'm not sure that I understand the data model you're describing. If you are only displaying members of one of your entity's to-many relationships as the content for the table's rows, then you can fetch only the properties on display in each of the visible rows using -setPropertiesToFetch: on your fetch request, like in the following example:
NSArray *propertiesToFetch = [[NSArray alloc] initWithObjects:#"title", #"thumbnailImage", nil];
[fetchRequest setPropertiesToFetch:propertiesToFetch];
[propertiesToFetch release];
However, if what you're describing is a list of entities, with one of the displayed elements in the table row being from a to-one relationship, you can use -setRelationshipKeyPathsForPrefetching: like Barry suggests. However, in that case I'd suggest denormalizing your data model and moving that property from being within a relationship to being directly within the original entity. Traversing relationships is much more expensive than accessing properties.
First, I would not assume that the default Core Data behavior is less performant than your proposed approach: without data to back up your efforts, optimization is almost certainly going to go awry.
That said, I believe -[NSFetchRequest setRelationshipKeyPathsForPrefetching:] will accomplish what you want.
You could manually fault in objects, but I don't think you'll gain anything. Whether you fault in all the objects at once, or you fault them in one at a time as needed, each object is still going to be faulted in individually.
I have written apps that do exactly what you describe, fault in a large amount of data to display in a table view, and have never noticed a performance penalty. Remember, only the objects that correspond to table view cells that will be displayed will be faulted in.
In general, I'd say don't try to outsmart Core Data. It's got years of performance optimizations in it at this point. While, intuitively, it may seem like faulting in 100 objects would require 100 database queries, this is not necessarily the case.