I have a managed object that has a one-to-one relationship to itself. An example of this could be a person object could be linked to one other person to represent a marriage.
If one of the properties of the person was address and it was changed for one person, how would I follow the relationship and make sure it was changed for the linked person object.
Initially I thought about doing this in the setter, i.e setAddress but I quickly realised that this would cause infinite recursion between the two objects.
What's an elegant solution to this type of problem?
You could still do that in the setter, but just check, if the destination value is already the one you want to set, return from setter, without any infinite loop.
You could try something like
- (void)setAddress:(NSString *)address
{
// common setter here
// then set your relationship's address
if (![self.personRelationship.address isEqual:address]) {
self.personRelationship.address = address;
}
}
Based on your description I would move the address into a separate entity Address that has a relationship to Person. Set up the relationship so that each person has one address and each address can be used by more than one person.
By doing this, you only need to change the address in one place, and then it immediately takes effect for any person who uses that address. If two people who have the same address later need to have different addresses, create a new Address entity for them.
1. Step
If you want to override the setter in CoreData you should follow the documentation here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdAccessorMethods.html#//apple_ref/doc/uid/TP40002154-SW14
The key in this is to use the "primitive" accessors like in the example:
- (void)setAddress:(NSString *)newAddress
{
[self willChangeValueForKey:#"address"];
[self setPrimitiveAddress:newAddress];
[self didChangeValueForKey:#"address"];
}
Then you will not get an infinite loop trying to set the attribute.
2. Step
Apply it to the address question you can set the partner's address as well.
The code will change to
- (void)setAddress:(NSString *)newAddress
{
[self willChangeValueForKey:#"address"];
[self setPrimitiveAddress:newAddress];
[self didChangeValueForKey:#"address"];
[self.partner willChangeValueForKey:#"address"];
[self.partner setPrimitiveAddress:newAddress];
[self.partner didChangeValueForKey:#"address"];
}
Alternative suggested by noa:
- (void)setAddress:(NSString *)newAddress
{
[self willChangeValueForKey:#"address"];
[self setPrimitiveAddress:newAddress];
if (![self.partner.address isEqual:newAddress])
[self.partner setAddress:newAddress];
[self didChangeValueForKey:#"address"];
}
Related
I am learning Objective-C, and as one of the lessons, we create a class Employee, and its superclass Person, along with an Asset class, whose instances are owned by Employee. To test the classes, and their instance functions, I wrote the following code
int main(int argc, const char *argv[]){
#autoreleasepool{
NSMutableArray *employees = [[NSMutableArray alloc] init];
for (int i = 0; i < 10; i++){
Employee *person = [[Employee alloc]init];
[person setWeightInKilos:90 + i];
[person setHeightInMeters:1.8 - i/10.00];
[person setEmployeeID:i];
[employees addObject:person];
}
.../*code continues from here, to assign [person]
's at random indexes in *employees, instances of the Asset class */
}
}
I was wondering what are the benefits or disadvantages to setting the [person] with one method, which in turn calls the other methods, like so:
main.m:
....
for (int i = 0; i < 10; i++){
Employee *person = [[Employee alloc]init];
[person setWeightInKilos:90 + i withAHeightOf:1.8 - i/10.00 andId:i];
[employees addObject:person];
}
....
Thanks.
Basically there isn't any right and wrong here, objective-c syntax and coding conventions allow you to have long messages with multiple parameters.
There are a couple of options here:
Setting these parameters on init (e.g. initWithId: height: weight:)
Creating a property for each of them
Creating an individual function for each of them
Creating a setProperties function
To decide this you should first ask yourself:
Does these triplets (Height, Weight, Id) belong together logically?
Does it make sense to set just one of them?
Does it makes sense that this object is missing these values?
Is there a need to make setting these parameters an atomic action?
Does Immutability of the object matters?
Most of the time I choose to make my object immutable, which means I create a readonly property for them, and set them via init function.
Your Person class has several properties including weightInKilos, heightInMeters, and employeeID.
You can, of course, set each property individually. As a convenience, you can provide special "helper" methods that make it easier to set several properties in one call. This is completely optional. Whether you choose to add these extra methods is strictly a matter of personal taste and style.
One consideration for such "helper" methods is where to draw the line. Which properties should be parameters to such methods? What happens if you later add another property? Do you update the "helper" methods to take another parameter and then update all code calling those methods?
In many cases it's better to just set each one individually. One guideline I use for deciding on whether to create such a "helper" method is if the object requires a certain set of properties to be set to be valid. If there is such a case then I usually create a special init method that takes the required properties as parameters. Then all of the optional ones can be set individually.
You neglected to show how you've defined the Employee object. Are weight, height and identification held as properties? If so, they generally will have accessor methods automatically generated for them so you will not need to write those routines. Properties have nice conveniences associated with them.
As to why you would use one method over another, the most compelling reason is a matter of personal style. Something to consider -- what happens if you choose to change the stored weight of a person; do you have a method to set only that parameter, or do you need to set everything about them?
It's common to have an initializer that receives the information to be initialized about an object, but something like - (id)initWithWeight:andHeight:andId:andSomethingElse:andMoreStuff: gets ugly far sooner than I've stretched it out. To solve this, the NSDictionary class can be helpful, where the dictionary contains all the fields you choose to include and the method becomes - (id)initUsingDictionary:(NSDictionary *)dictionary;. A nice benefit to this is you can easily make fields optional and a single method signature handles it.
I have implemented awakeFromInsert to set some default values and relationships in my core data objects. However, the method is being called twice, meaning that the to-many values I am adding are being added multiple times.
I am using parent and child managed object contexts.
What gives?
awakeFromInsert will be called when you insert the object into its initial context. When this context is saved and the changes are pushed up to the parent context, it will be called again.
You can query the self.managedObjectContext property to determine which case the method is being called for. Depending on your particular use case, you may want to check for the presence or absence of a parentContext and act accordingly.
thanks to jrturton help:
here is the simplest one:
when parentContext is null, means when this context is saved you can do you custom logic, for example incrementing table number
- (void)awakeFromInsert
{
if (!self.managedObjectContext.parentContext) {
//setting tableNumber
[self willChangeValueForKey:#"number"];
[self setPrimitiveNumber:tableNumber];
[self didChangeValueForKey:#"number"];
}
}
One of my NSManagedObject's attribute depend on various attributes, some of them in a related NSManagedObject.
In a first run, I implemented a simple Transient Attribute for such attribute, but I just discover that it's not possible to use fetch predicate with transient properties.
I need to create an attribute so that:
Its value it's calculated using different attributes
1 of the dependent attribute is present in a related NSManagedObject
I can do fetch using this attribute as predicate.
If I update one of the dependent attributes, the calculated value must be updated
You can use Key value observing to monitor a property being changed in order to keep some calculated property up to date. You can add an observer to the property you want to monitor.
KVO Programming Guide.
edit: Reference here
To do this correctly, you'll want to override the property getter for your calculated property, and create a keyPathsFOrValuesAffecting<Key> function.
Apple's example is pretty good, it gives a case where a fullName property should be gathered from the firstName and lastName.
So you would need to implement KVO with this function:
+ (NSSet *)keyPathsForValuesAffectingFullName
{
return [NSSet setWithObjects:#"lastName", #"firstName", nil];
}
This will allow your app to be notified anytime those values are modified. Then you'd just override the getter that would be called upon this notification
- (NSString *)fullName
{
return [NSString stringWithFormat:#"%# %#",firstName, lastName];
}
I have a simple Core Data iPhone app where Parents have a one-to-many relationship with Children. In the first view, you are presented with a list of Parents; tapping on one provides the corresponding list of Children. In the Child view, I want the each cell to show the Child name, and then the Parent name as a subtitle. I have used the following command:
cell.detailTextLabel.text = [[managedObject valueForKey:#"Parent"] description];
But instead of getting the parent name, the subtitle displays something like:
<Parent: 0x4d5a520> (entity: Parent; id...
Obviously I'm printing out the actual relationship, rather than the object's name. How can I show the actual Parent name ("Mr Smith")?
Thanks.
I managed to work it out! I'm gradually getting the hang of this programming thing... :-)
I'll leave the answer here in case someone else searches for it one day...
Instead of using a generic NSManagedObjectContext with KVC, I used my own subclass:
Child *child = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [child.name description];
cell.detailTextLabel.text = [child.parent.name description];
Your basic problem is that description is a method of NSObject that returns a description useful for programmers. If you have an attribute called description then it will collide with the built-in description method. You may have been confused because when you call description on a NSString, you get the string but if you call any other class, you don't get the literal data e.g. When you call description on a managed object you get the object UUID, whether it is a fault or not, it's attributes and the objects it's related to. All that is useless for anything but debugging.
Never use description as an attribute name and never use the return of description for anything the user ever sees.
If you have a tableview of Child objects as described above then your fetched results controller will return a managed object configured to the Child entity. To access the name of the related Parent object you would use:
cell.detailTextLabel.text = [[childMo valueForKey:#"parent"] valueForKey:#"name"];
Of course, if either the relationship or the parent.name attribute is optional, you should first check that either has a value before attempting to use the value.
I've been attempting this for two days, and constantly running into dead ends.
I've been through Aaron Hillegass's Cocoa Programming for MAC OS X, and done all the relevant exercises dealing with NSTableview and mutable arrays, and I have been attempting to modify them to suit my needs.
However none of them seem to be using an array with objects as a data source, it seems to use the tableview as the datasource.
I'm trying to implement Jonas Jongejan's "reworking" of my code here, with a Cocoa front end to display the results.
Any pointers or suggestions I know this should be simple, but I'm lost in the wilderness here.
I can populate the table by setting the array
It's pretty simple really, once you get to understand it (of course!). You can't use an NSArray directly as a table source. You need to either create a custom object that implements NSTableViewDataSource or implement that protocol in some existing class - usually a controller. If you use Xcode to create a standard document based application, the document controller class - (it will be called MyDocument) is a good class to use.
You need to implement at least these two methods:
– numberOfRowsInTableView:
– tableView:objectValueForTableColumn:row:
If you have a mutable array whose values you'd like to use in a table view with one column, something like the following should do as a start:
– numberOfRowsInTableView: (NSTableView*) aTableView
{
return [myMutableArray count];
}
– tableView: (NSTableView*) aTableView objectValueForTableColumn: (NSTableColumn *)aTableColum row: (NSInteger)rowIndex
{
return [myMutableArray objectAtIndex: rowIndex];
}
It has just occurred to me that you could add the above two methods as a category to NSArray replacing myMutableArray with self and then you can use an array as a data source.
Anyway, with a mutable array, it is important that any time you change it, you need to let the table view know it has been changed, so you need to send the table view -reloadData.
If your table view has more than one column and you want to populate it with properties of objects in your array, there's a trick you can do to make it easier for yourself. Let's say the objects in your array are instances of a class called Person with two methods defined:
-(NSString*) givenName;
-(NSString*) familyName;
and you want your table view to have a column for each of those, you can set the identifier property of each column to the name of the property in Person that that column displays and use something like the following:
– tableView: (NSTableView*) aTableView objectValueForTableColumn: (NSTableColumn *)aTableColum row: (NSInteger)rowIndex
{
Person* item = [myMutableArray objectAtIndex: rowIndex];
return [item valueForKey: [tableColumn identifier]];
}
If you replace valueForKey: with valueForKeyPath: and your Person class also has the following methods:
-(Person*) mother;
-(Person*) father;
-(NSString*) fullName; // concatenation of given name and family name
you can add table columns with identifiers like: father.fullName or mother.familyName and the values will be automatically populated.
You could go the datasource route and do all of the heavy lifting yourself, or you could let bindings do all the heavy lifting for you. Add an NSArrayController to the nib file that has the table view in it. Make sure that the File's Owner of the nib is set to the same class that has the mutable array in it. Bind the contentArray of the array controller to File's Owner.myMutableArray. For each column bind Value to the array controller arrangedObjects and add the appropriate key path. This will allow you to get things like user sorting for free if you ever need it.
On the iPhone (I know you're talking about Mac, but maybe this could help) you have to use delegation for loading a tableView. It asks for a cell and you use your array to fill-in the data where needed.
I'm not sure if this works for the Mac, but it'd be worth looking into.
Maybe set dataSource to self and use those delegate methods to access your array based on the row and column #
Apple has a whole guide for Table View Programming so I suggest you start with the Using a Table Data Source section of the that guide.