Core Data – NSManagedObjects in instance variable update - objective-c

Say that I perform a fetch of all entity Employee objects by doing: NSArray *employees = [context executeFetchRequest:request error:&error];
Then I set an instance variable in my class by doing: self.allEmployees = employees;
Then later in my app I will do some modifying to my employee objects but not through accessing self.allEmployees. I'm modifying them from another class.
Will my self.allEmployees array be updated to the changes I've made from another class to the employee objects? Or will my self.allEmployees array be in the state that the employee objects were when I first performed the fetch? (I'm suspecting the later)

The array you get back from the fetch request holds references to live managed objects. Unless you change a different fetchLimit or batchSize you get an array with as many fault objects as the query would return objects.
When you access a property of one of these fault objects CoreData retrieves the actual data for all the properties transparently and returns these to you.
A managed objects always has its own most recent value that was last called save on. So if you do a modification on the self.allEmployees you need to call save on the MOC and this will broadcast the changes to all other emoployee objects.
This is also the reason why KVO works on NSManagedObject properties, because they get notfified of all saved changes that affect them.

If you alter the objects you receive from the fetch, and don't copy them, then yes.
Those are all pointers.
So you shouldn't need to do another fetch request.
If you change values of an employee, the pointer won't change.
The only thing that changes is the instance variables, or properties.

Related

Core data: executeFetchRequest vs performFetch

I want a thorough list regarding comparison between the two. Things I have known:
executeFetchRequest:
Message sent to MOC
Return an array of managed objects
Goal: fetch objects from persistent store to MOC
With table view: has nothing to do with table view
Frequency: often used in a loop, so could be called many many times
performFetch:
Message sent to FRC
After calling it, use fetchedObjects to return an array of managed objects
With table view: FRC is specifically for keeping managed objects and table view rows in sync, and use performFetch to initialize that process.
Frequency: often only once. Unless fetch request of FRC changes, no need to call performFetch a second time
Please correct me if I am wrong and append the list. Thank you.
About executeFetchRequest:
Message sent to MOC
Yes
Return an array of managed objects
Yes, but you can also change the type of results you want to retrieve. In NSFetchRequest you can set a different result type with:
- (void)setResultType:(NSFetchRequestResultType)type
where NSFetchRequestResultType can be of different types. Taken from Apple doc:
enum {
NSManagedObjectResultType = 0x00,
NSManagedObjectIDResultType = 0x01,
NSDictionaryResultType = 0x02
NSCountResultType = 0x04
};
typedef NSUInteger NSFetchRequestResultType;
Goal: fetch objects from persistent store to MOC
Yes, creating a NSFetchRequest and performing a request, it the same as creating a SELECT statement in SQL. If you also use a NSPredicate it's the same as using SELECT-WHERE statement.
With table view: has nothing to do with table view
Yes, but with retrieved data you can populate a table
Frequency: often used in a loop, so could be called many many times
It depends, on what you want to achieve. It could be within a loop or not. Executing the request within a loop could have impact on performance but I would not be worried on that. Under the hood Core Data maintains a sort of cache mechanism. Every time you perform a request, if data are not in the cache, Core Data executes a round trip on your store (e.g. sql file) and populate the cache with the objects it has retrieved. If you perform the same query, the round trip will not performed again due to the cache mechanism. Anyway, you could avoid to execute a request within the run loop, simply moving that request outside the loop.
About performFetch:
Message sent to FRC
Yes
After calling it, use fetchedObjects to return an array of managed
objects
Yes, but you can also retrieve an object with [_fetchedResultsController objectAtIndexPath:indexPath]; if you are populating a specific cell within a table.
Here I really suggest to read a nice tutorial on NSFetchedResultsController
With table view: FRC is specifically for keeping managed objects and
table view rows in sync, and use performFetch to initialize that
process.
Yes, a NSFetchedResultsController works in combination with a NSManagedObjectContext for you. Furthermore, it enables lazy loading of data. Suppose you have 1000 elements you retrieve and you want to display them in a UITableView. Setting a request for a NSFetchRequest like:
[fetchRequest setFetchBatchSize:20];
and using it with an instance of a NSFetchedResultsController, it allows to load 20 elements at first. Then when you scroll, other 20 elements are loaded, and so on. Without a NSFetchedResultsController you must implement this behavior manually. Refer to the tutorial I provided for further info.
Frequency: often only once. Unless fetch request of FRC changes, no
need to call performFetch a second time
It depends on what you want to achieve. Most of the time you could call it once.
Hope that helps.
Edit
You have to call performFetch explicitly. I like to create a property for NSFetchedResultsController in my header file (.h) like
#property (nonatomic, strong, readonly) NSFetchedResultsController* fetchedResultsController;
and synthesize it in your implementation file (.m) like
#synthesize fetchedResultsController = _fetchedResultsController;
Then always within the .m file override the getter to create an new instance of it:
- (NSFetchedResultsController*)fetchedResultsController
{
// it already exists, so return it
if(_fetchedResultsController) return _fetchedResultsController;
// else create it and return
_fetchedResultsController = // alloc-init here with complete setup
return _fetchedResultsController;
}
Once done, within your class (for example in viewDidLoad method) use it like
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
// Handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
You are comparing the wrong elements. NSFetchedResultsController uses the NSManagedObjectContext to perform the fetch, and under proper configuration, monitors the changes in the managed object context to verify the status of the fetch properties it is monitoring, but the actual fetches are done by the context. On both cases, NSManagedObjectContext does the fetch. The difference being that, using the NSManagedObjectContext directly, you get an NSArray type of object (the actual runtime class is different than an array you get using [NSArray array]), while NSFetchedResultsController has a different purpose (have a collection of results and monitor changes to the records and entity on its fetch request). In other words, NSFetchedResultsController works using the context, but it works different than just a simple collection of objects.
One observation: you shouldn't be using executeFetchRequest inside a loop, especially calling it "many many times". Each fetch has its performance cost. You can call executeFetchRequest once, and do a loop to check the result.

Parent class of my array

I have an object which contains an array.
Without coredate or a database, how do I model the relationship back to the parent from any of the children. Do I have to explicitly store the parent id in the child? Or is there a way to get the "parent" class of this array?
Objective-C (unlike Qt and a few other object models) has no "parent-child" or "ownership" relationship. Yes, sometimes we say that the object that retains an object "owns" it, but that's more of a shared ownership, since multiple objects can retain a given object, but none of the "owners" is more "distinguished" than the others and hence (in the plain Objective-C model) the "parent".
The first thing to consider is whether your "parent/child" concept really makes sense in this multiple-owner environment. If so, then you need to maintain the child-to-parent pointers yourself, somehow.
Without subclassing NSArray and adding an extra property, which I think is a bad idea, I guess you have two choices:
Make the object at index 0 a pointer to your containing object
Don't store your array directly in your "parent" object, store it in a NSDictionary which also has a #parent key, holding a reference to the parent.
To be honest I'm not sure what you are trying to achieve here - when would you need to find out this information that wouldn't be better done by simply working with the parent object itself?
UPDATE
I've just seen this answer to a separate question: Create Custom UIButton class
Associative references sound like they could be what you need? You can associate a reference to your containing object to the NSArray. I don't know anything else about them, though, I just saw that answer and thought of this question.
NSArray * array = [[NSArray alloc]init];
id parrent = [array super];

NSFetchRequest not catching objects that have a changed property

I have run into a weird problem with CoreData on MacOsX 10.6 using an SQL store. I have an NSManagedObject subclass called Family with attribute name and a relationship personList connected to another NSManagedObject subclass called Person with attribute firstname and inverse relationship family. A Person has only one family, and a family can have several Persons.
Say I have a Family object family pointing to the family 'Doe' with 2 Person (John and Jane) connected to it and I do the following request:
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:#"Person" inManagedObjectContext:managedObjectContext]];
[request setPredicate:[NSPredicate predicateWithFormat:#"family.name=%#",[family name]]];
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
I get an array of size 2 with the 2 persons: Jane and John, with Family name Doe. Now, if I update the Family using its synthesized accessor, in my case:
[family setName:#"Wheat"]
I cannot after get the list of Person using the same fetch request. The results is an [array count] of 0.
If I change the predicate to the following line, it works again:
[request setPredicate:[NSPredicate predicateWithFormat:#"family=%#",family]];
So it is as if the Predicate is not using the updated version of the property name of the family, even though I have the NSFetchRequest set to the defaults (so includesPendingChanges returns YES). That makes no sense to me. It seems that the NSFetchRequest finds the family object, but fails to see that its value family.name has been updated, not saved, and is in the managedObjectContext in memory. Of course, if I save the store, then it works again.
Any idea? I have been through the Mac documentation and cannot see why this would fail.
I think the key here is understanding the fetch request. It retrieves data from the persistent store, so clearly, if you did not save to the persistent store, it will not find that data. The situation you describe is entirely logical if you take that into account.
From the Core Data Programming Guide:
You cannot fetch using a predicate based on transient properties
(although you can use transient properties to filter in memory
yourself). Moreover, there are some interactions between fetching and
the type of store—for details, see “Store Types and Behaviors.” To
summarize, though, if you execute a fetch directly, you should
typically not add Objective-C-based predicates or sort descriptors to
the fetch request. Instead you should apply these to the results of
the fetch. If you use an array controller, you may need to subclass
NSArrayController so you can have it not pass the sort descriptors to
the persistent store and instead do the sorting after your data has
been fetched.
To summarize, I have been testing thoroughly my code and here is how I perceive the limitations of CoreData regarding Fetching and objective-c predicated (ie the dot notation).
If an object has been access by the Objective-C program and if one of its property or relationship has been modified, any NSFetchRequest with a predicate using a dot notation will return the structure of the SQL store, hence the results will be erroneous.
In the case of the trivial Family and Person example, if you have a link to a Family and change its name, any query made on the Person NSEntity cannot include a predicate with the following query item
#"family.name=%#"
It will indeed query using the family name in the SQL store. However, the following query will work after such a change:
#"family=%#"
Indeed, the NSFetchRequest will still retrieve the info in the store, but since the structure has not changed, it will replace the objects retrieved by those in Memory, so a subsequent test to [family name] will return the updated name.
With care, you can use nested Predicate such as:
#"person.family.name=%#"
As long as you can guarantee that all the objects that have the property person, have not their family altered, nor their name altered. If it's not the case, then you can at best call
#"person.family=%#"
Or if you can't guarantee that all the Family objects are untouched, only
#"person=%#"
Of course, an alternative is to systematically SAVE: the NSManagedObjects to the persistent store every time you make any change, so all properties are updated and then all the above notations would work. There are times however when you do want to prevent savings and force the customer to change it's document only if he wishes (think about Word, Excel, Picture tools, etc..). Hope this is of help.
If by "using the same fetch request", you mean using the very same instance of the fetch request that you constructed the first time, then this is no surprise. The predicate you applied is "family.name = Doe". Once the family's name is "Wheat", the fetch request's predicate no longer matches it, because "Wheat" != "Doe".
To retrieve the family after changing its name, you would need to create a new instance of NSFetchRequest using a predicate matching the new family name.
If by "using the same fetch request", you mean using a different fetch request constructed using the same code, well, then I would think about #Mundi's answer.

How to get the ID of an object saved to Core Data's managed object context?

I have this code:
NSEntityDescription *userEntity = [[[engine managedObjectModel] entitiesByName] objectForKey:#"User"];
User *user = [[User alloc] initWithEntity:userEntity insertIntoManagedObjectContext:[engine managedObjectContext]];
and I want to know the id of the object inserted to the managed object context. How can i get that?
Will that id remain the same for that object's lifetime or will it persist to the sqlLite database beneath this and be something that can be used to uniquely identify it during a fetch operation (my ultimate goal).
Any help appreciated // :)
If you want to save an object's ID permanently you need to:
Save the object into the context so that the ID changes from a temporary to a permanent ID.
Extract the URI version of the permanent ID with -[NSManagedObjectID URIRepresentation]. That returns a NSURL you can store as transformable attribute in another managed object.
You can get the object by using -[NSPersistentStoreCoordinator managedObjectIDForURIRepresentation:] to generate a new NSManagedObjectID object and then use -[NSManagedObjectContext objectWithID:] to get the actual referenced managed object.
The URI is supposed to identify a particular object in a particular store on a particular computer but it can change if you make any structural changes to the store such as migrating it to a new model version.
However, you probably don't need to do any of this. ObjectIDs play a much smaller role in Core Data than they do in other Data Model systems. Core Data maintains an object graph that uniquely identifies objects by their location in the graph. Simply walking the graph relationships takes you to a specific unique object.
The only time you really need ObjectID is when you're accessing object across two or more persistent stores. You need them then because relationships don't cross stores.
Read up on "managed object IDs" in the Core Data Programming Guide
You can get the object id from the object with something like:
NSManagedObjectID *moID = [managedObject objectID];
First, you are constructing your objects in a non-preferred manner. Generally you should:
User *user = [NSEntityDescription insertEntityForName:#"User" intoManagedObjectContext:[engine managedObjectContext]];
Second, when you create the object it will get a temporary id which you can access via [user objectID] as David mentioned. Once you save the context then it will get a new "permanent" id.
However this id can and does change over the lifetime of the entity (although not the instance). Things like migrating the data can cause this id to change. However, between saving the context and exiting the application the id will remain the same.

NSManagedObjectContext returns YES for hasChanges when there are none

I created a separate NSManagedObjectContext on a separate thread to perform some store maintenance. However, I have noticed that the context returns YES for hasChanges as soon as a managed object in it is even referenced e.g.
NSString *name = managedObject.name;
This context is created and used exclusively in 1 method. Why is it returning has changes, when there there are none?
That ks difficult to answer without seeing the code. Perhaps your object has a -awakeFromFetch call that touches a property or something else. Normally there should be no changes from just fetching an object unless you are doing something to that object either in the awakeFromFetch or somewhere else in your code.
update
Before the save, grab the deleted array, updated array and inserted array and take a peek at them. That will give you a hint as to what is going on.