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.
Related
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. ;-)
My iPhone app has a summary page (UITableView) where I would like to show a quick overview and therefore I need to get info from several entities. It was suggested to create an abstract parent entity and have my two entities as children of this one. This do allow me to fetch the two entities using the one fetchedresultscontroller.
This works but I find that I need to filter the return a small bit. Because of the 'hack' above these entities have nothing in common so I need completely separate predicates.so from EntityA I would need "color = blue" and from EntityB "length >= 10". Because the entity I'm actually querying have none of these this doesn't work at all.
Is there a way to do this or what's the best approach here?
Niether the UITableView or the NSFetchedResultsController is designed to deal with more than one entity at a time. Moreover, it seldom makes any sense to try to do so. If you find yourself in such a situation, you probably need to rethink your data model design.
If entities are logically associated with each other then they should be linked by a relationship. If data from any two objects is to be displayed in the same tableviewCell without being gibberish then they must have some logical association and therefore should be linked by a relationship of some kind. To display in the table, you fetch one side of the relationship and then walk the relationship/s to find the other related objects.
If the logical association is strong, it should be defined as a formal relationship in the data model e.g.:
EntityA{
//... some attributes
b<-->B.a
}
EntityB{
//...some attributes
a<-->A.b
}
However, if the relationship is weak or transient, then you should use a fetched property to relate them. The fetched property dynamically searches for other entities based on a preprogrammed fetch.
EntityA{
creationDate:date
someBs--(creationDate=$FETCH_SOURCE.creationDate)-->B
}
EntityB{
creationDate:date
}
A key concept here is that Core Data is providing the entire model layer of your Model-View-Controller design. It is not just a dumb database but an active object graph that models both the data itself and its behavior. Once you have a properly designed data model, problems with the controllers and views resolve themselves automatically.
If i understand correctly, you can use notifications and send a dictionary of required information to the UITableView view controller class.
I have a CoreData database full of objects that relate to each other.
EntityFoo - attribute: uniqueId, relationship: one-to-many to other EntityFoo objects, relationship: one-to-many to other EntityUnresolvedRelationship objects
EntityUnresolvedRelationship - attribute: uniqueId, inverseRelationship: back to EntityFoo object
When my iPhone application starts, I get the information for these objects from a web service. I parse the web service response and create the Foo entities. At the time I download and parse the data and put the Foo objects into CoreData I don't want to take the time to find the other EntityFoo objects that need to be related to this object, and it is likely that I do not yet have a Foo object to relate yet if it has not yet been downloaded and parsed, so I quickly make an UnresolvedRelationship object for the relationship and store the uniqueId in it so that I can resolve the relationship for this object later.
Now I am trying to figure out the most efficient way to walk through all of the Foo or UnresolvedRelationship objects and create the proper CoreData relationships between all of the Foo objects. In other words, in the end I want to have no UnresolvedRelationship objects...this was just temporary...the Foo objects will only have relationships to each other.
There could be 15,000 plus Foo objects.
Is there a good way to fetch all of the Foo objects and all of the UnresolvedRelationship objects in a way that I can walk one of the arrays and quickly find the matching entity in the other array by it's uniqueId so that I can setup the CoreData relationship?
Anyway, would love any pointers.
you should be doing this as part of the parsing of the data coming from the web service. That is going to be the most efficient way to do it. The way you are approaching this is that you are guessing that the relationship lookup is going to be slow. That is a bad approach to optimization.
Fetching the objects and linking them is not going to be slow unless you cause them to be realized (faulted) into memory. If you are faulting 15K objects, it is going to be slow anyway.
Set up you relationships while parsing, use autorelease pools effectively during your parsing and reset your context at consistent intervals during the parsing to keep memory down. That is the best option.
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.
What is the best way to implement arbitrary row reordering in a tableview that uses core data? The approach that seems obvious to me is to add a rowOrder attribute of type Int16 to the entity that is being reordered in the tableview and manually iterate through the entity updating the rowOrder attributes of all the rows whenever the user finishes reordering.
That is an incredibly inelegant solution though. I'm hoping there is a better approach that doesn't require possibly hundreds of updates whenever the user reorders things.
If the ordering is something that the data model should modal and store, then the ordering should be part of the entity graph anyway.
A good, lightweight solution is to create an Order entity that has a one-to-one relationship to the actual entity being ordered. To make updating easy, create a linked-list like structure of the objects. Something like this:
Order{
order:int;
orderedObject<--(required,nullify)-->OrderObject.order
previous<--(optional,nullify)-->Order.next;
next<--(optional,nullify)-->Order.previous;
}
If you create a custom subclass, you can provide an insert method that inserts a new object in the chain and then sends a message down the next relationships and tells each object to increment its order by one then the message to its next. A delete method does the opposite. That makes the ordering integral to the model and nicely encapsulated. It's easy to make a base class for this so you can reuse it as needed.
The big advantage is that it only requires the small Order objects to be in alive in memory.
Edit:
Of course, you can extend this with another linked object to provide section information. Just relate that entity to the Order entity then provide the order number as the one in the section.
There is no better way and that is the accepted solution. Core Data does not have row ordering internally so you need to do it yourself. However it is really not a lot of code.