subclassed NSManagedObject does not call description when NSLog'd - objective-c

I have a data model which has two entities in a relationship of one to many.
Each entity has a class that is subclassed from NSManagedObject.
I get the relation set for the one entity and walk it casting each set member to the specific subclass as I enumerate the collection.
When I do
NSLog(#"My Entity: %#", myEntityInstance);
It logs but does not call my subclass's method for:
- (NSString*) description
It does call if I send:
NSLog(#"My Entity: %#", [myEntityInstance description]);
Any ideas what is being called and why description has to be manually called?
Thanks!

If a class instance responds to descriptionWithLocale:, then NSLog will use that instead. Although descriptionWithLocale: does not appear in NSManagedObject's list of instance methods, it could possibly still be implemented.
Try overriding descriptionWithLocale: and see if that makes a difference.
- (NSString *) descriptionWithLocale:(id) locale
{
return #"my description";
}

I've never seen that. I don't think it's a NSManagedObject behavior. You might log the class before making the call to make sure your instance is of the class you think it is.

Might be two years or so late to the game, but for the benefit of others, I had this problem tonight. The cause was that, while I had made NSManagedObject subclasses for my entities, one of the entities in the CoreData Modler had it's "Class" set back to NSManagedObject instead of the custom subclass.
It didn't matter what I put into -description in my subclass files, because the objects were coming out of the Context as NSManagedObjects instead of my custom subclass.
Putting the subclass name back in the Entity Inspector in the Xcode Coredata Model Editor fixed it.

Related

Subclassing a Subclass of PFObject

I have utilized subclassing of PFObject for a few classes in my code including the following classes:
MGRound:PFObject - a class that represents a golf round
MGCurrentRound:MGRound - a singleton class that holds data for the current round being entered. As you can see it is a subclass of MGRound
When I try to instantiate an object of class MGRound using [MGRound object] the object that I am getting is of the class MGCurrentRound.
Has anyone else run into this? Any suggestions?
The issue noted above was not my problem. I had to use the valueForKey method because the objects were PFObjects, not my subclasses. I was trying to use dot notation was which incorrect.

What is the point of key-value coding?

Since Objective-C 2.0, we've had #properties and autogenerated accessor methods. So today, what is the point of key-value coding? Under what circumstances is it preferable to write
[myObject setValue:foo forKey:#"bar"];
instead of writing
[myObject setBar:foo];
or even
myObject.bar = foo;
I keep seeing articles and documentation making use of KVC, but always in a way where it seems like simply using properties would be better. So why would I ever use KVC? Thanks for any and all insight.
It's almost never preferable to write out [myObject setValue:foo forKey:#"bar"] by hand, with a literal #"bar". We usually use KVC to access a property when we don't know which property we want to access until runtime.
One example is an outlet in a xib. When you connect a text field's delegate outlet to the File's Owner in the xib, the xib records the connection as an object with three fields:
a reference to the text field (the object that has the outlet)
a reference to the file's owner placeholder (the destination of the connection)
the name of the outlet as a string, #"delegate"
At runtime, the xib loader (part of the UIKit framework) deserializes the text field. Then it deserializes the connection object and uses it to establish the connection that you wired up in the xib. The xib loader has to set a property of the text field (the delegate property), but it doesn't know which property until it loads the xib at runtime, long after both your app and the UIKit framework were compiled.
Another example of not knowing which property to access until runtime is the (little-known) ability of Core Animation to animate a custom property of your CALayer subclass. Say you create a subclass of CALayer called PolygonLayer, with a property named sides. You can animate the sides property using a standard CABasicAnimation:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"sides"];
animation.fromValue = #3;
animation.toValue = #9;
animation.autoreverses = YES;
animation.duration = 1;
[myPolygonLayer addAnimation:animation forKey:#"sides"];
Presto, Core Animation will animate your layer's sides property from 3 to 9 and back. Yet the source code of Core Animation doesn't know anything about your sides property. (Check out this question for more details.)
There are times we use KVC even though we know the property at compile-time. One example is when we want to take advantage of extra work KVC will do for us. For example, if you have an NSArray full of Person objects, and you want to get an array of every person's first name, you could write this:
NSMutableArray *firstNames = [NSMutableArray array];
for (Person *person in people) {
[firstNames addObject:person.firstName];
}
But this is a case where KVC has a feature that makes it simpler. If you access a property of an array using KVC, KVC will actually access that property of every element in the array for you:
NSArray *firstNames = [people valueForKey:#"firstName"];
Another example where we might use KVC even though we know the property at compile-time is when the property is not statically declared as part of the class. For example, each NSManagedObject (part of Core Data) dynamically gives itself properties based on whatever entity that instance of NSManagedObject is representing. You can access those properties using KVC, although generally we prefer to declare them in a subclass of NSManagedObject or in a category of NSManagedObject.
KVC can be useful if you are using Key Value Observers to detect value changes on an object. If you wanted to use KVO and #properties you would have to wrap every mutator method with:
[self willChangeValueForKey:#"bar"];
bar = foo;
[self didChangeValueForKey:#"bar"];
Sometimes you don't know what property you want to set/get until run-time.
In this case you can use KVC by constructing the property key path as a string.
For example i have an object with multiple NSArray properties and i want to keep the last NSDate they were updated.
Let's say i have an array property called: comments and an array property called likes.
I define a properties: commentsLastModified and likesLastModified.
when an array is updated (i have the property name as string), i use:
[object setValue:[NSDate date] forKey:[NSString stringWithFormat:#"%#%#", arrayString, #"LastModified"];

Creating and using a dummy NSData subclass doesn't work

I have a problem with creating my own subclass of NSData, which I want to have a custom description method. Even creating a dummy NSData subclass:
#interface MyData : NSData {}
#end
and
#implementation MyData
#end
and using it results in weird bugs (the function that uses it never exits, and control somehow returns to the run loop). I thought that maybe I am responsible for rewriting the designated initializers of NSData (calling the super implementation), but none is mentioned in the doc. So:
what are the designated initializers of NSData?
what is the bare minimum I need to write for a dummy subclass of NSData?
Making an NSData subclass is difficult because (as drewag noted) it is a member of a class cluster. From the Binary Data Programming Guide:
...data objects are not actual instances of the NSData or NSMutableData classes but instead are instances of one of their private subclasses.
When you do [[NSData alloc] initWith...] you don't get back an NSData; you probably get back an NSConcreteData. The extraordinary Cocoa With Love has a discussion and demonstration of subclassing class clusters.
The best (and most idiomatic) option is probably composition: your custom class should simply contain an NSData ivar, and implement a description method that operates on that enclosed object.
While drewag's response is technically correct, this is a dangerous technique to use on Cocoa classes; it will override the description method of every NSData object in the program, whether you create it directly or not.
In the specific case of the description method this may be okay, but for another method more likely to be relied upon by other objects in the framework, it could cause large, hard-to-trace problems. You should only do this if you are sure that there is no other way.
It would be far better to create a category and method with a prefix:
#interface NSData (FX_Description)
- (NSString *)FX_description;
#end
The Apple docs specifically mention this category-override technique and advise against it:
Because the methods declared in a category are added to an existing class, you need to be very careful about method names.
If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime.
An earlier version of the docs went on to say:
The very presence of some category methods may cause behavior changes across all frameworks. For example, if you override the windowWillClose: delegate method in a category on NSObject, all window delegates in your program then respond using the category method; the behavior of all your instances of NSWindow may change. Categories you add on a framework class may cause mysterious changes in behavior and lead to crashes. [Emphasis mine.]
If all you want is to override a single function "description" consider using a "Category" instead:
#interface NSData (MyData)
-(NSString*)description;
#end
#implimentation NSData (MyData)
-(NSString*)description
{
return #"something";
}
#end
Then, you can use this function on any instance of NSData.
It is very difficult to subclass NSData because it is a "Class Cluster." The public API treats it as one class, but in reality it is a collection of hidden subclasses. You can research overriding a class cluster, but it is almost never needed. Another option is to create your "MyData" class with NSData as a member variable instead of using a subclass.

Why can I not call Core Data Accessors on super?

Background
I'm using the rather excellent mogenerator to auto generate my core data accessors.
mogenerator structures the classes as following:
NSManagedObject
_JGTrainingBase
JGTrainingBase
_JGTrainingGroup
JGTrainingGroup
Classes starting with an underscore are machine generated with core data accessors by mogenerator.
Classes without an underscore are human editable ones so you can put custom methods in there and not have it overwritten when you change your data model and rerun mogenerator.
The training group entity has to-many relationship called "children".
I'm using the Core Data accessors to modify my relationships.
What I Want
I want to update duration - a transient attribute - before adding a children object.
The Problem
My Code
#implementation JGTrainingGroup
...
-(void)addChildrenObject:(JGTrainingGroup *)value_ {
[self updateDuration];
[super addChildrenObject:value_];
}
...
#end
But when I call this method, I get an error message:
[JGTrainingGroup addChildrenObject:]: unrecognized selector sent to instance 0x10667fa30
Generated Code
#interface _JGTrainingBase : NSManagedObject {}
// Method declarations
#end
#interface _JGTrainingBase (CoreDataGeneratedAccessors)
- (void)addChildrenObject:(JGTrainingBase*)value_;
- (void)removeChildrenObject:(JGTrainingBase*)value_;
// Lots more methods
#end
Questions
Why does super not respond to the addChildrenObject: method? Is it something to do with these being added in a category style?
How can I access the Core Data generated method from a subclass?
Note
I realise I can add the children object to the set using primitiveValueForKey: and similar, but that means I'm rewriting the core data accessors, thus wasting my time and probably making my code buggy. I trust Apple's methods would be better than anything I could write.
Thanks for any input on helping me understand what's going on here.
If you have two subclasses of NSManagedObject where the parent class implements a dynamic property and its subclass (the grandchild of NSManagedObject) overrides the methods for the property, those overrides cannot call super.
says ADC
You can not call super as those accessors are generated on demand by the runtime

How to refer to the calling class in Objective-C

Can you refer to the sender of a message without passing the sender as a parameter?
This is simplified code for the sake of discussion:
// mainTableViewController.m
[dataModel loadData]; //Table is requesting data based on user input
// dataModel.m
-(void) loadData{
// I want to store the sender for later reference
sendingTableViewController = ???? ;
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
// Web data is loaded. Ask the sending tableViewController to
// reload it's data.
[sendingTableViewController.tableView reloadData];
}
I'm still getting used to how to refer to methods and properties that are the responsibility of another object. I want to send a message to dataModel to load some data using NSURLConnection. But I don't just want to return the data because I don't want to sit around waiting for the data to load. I want to send a message to the mainTableViewController once connectionDidFinishLoading is called.
Since the loadData method may be called from any number of tableViewControllers I can't just say [mainTableViewController reloadData].
Follow-Up Question
Great Information! I love the no-judgement nature of StackOverflow.
So the mainTableViewController would be the Delegate of the dataModel?
Would it be correct to say that the dataModel class defines the informal protocol?
I currently instantiate my dataModel class from within my mainTableViewController. So I could change my code like this:
// mainTableViewController.m
dataModel *myDataModel = [[dataModel alloc] initWithDelegate:self ];
// Does this method need to be defined in the mainTableViewController header file
// since I will already have defined it in the dataModel header file?
-(void) dataDidFinishLoading {
[self.tableView reloadData];
}
// dataModel.m
-(id) initWithDelegate:(id)aDelegate{
self.delegate = aDelegate;
}
-(void) connectionDidFinishLoading:(NSURLConnection *)connection {
[self.delegate dataDidFinishLoading];
}
Is it bad that my TableViewController is instantiating my dataModel, cause then my dataModel is owned by the TableViewController? Should I really instantiate the dataModel from the AppDelegate instead?
Thank You!
I daresay this isn't the correct way to think about the problem. Architecturally, by giving the data model knowledge of the table view controller, you are coupling your model layer to your controller layer. Violating separation of concerns is a bad thing.
In Cocoa, the use of delegate objects is used all over the place. A delegate object is an object that implements a particular protocol with callback methods that can be called when things or events (such as data loading from a remote location, in your case) occur. I recommend that you create a delegate property in your data model, have an interface that mainTableViewController (or any other class, really) implements, and assign that class as the delegate. Then, when the data is finished loading, call the appropriate method on self.delegate. In that callback method, you could then call [tableView reloadData].
Again, you do not want your data model to be coupled (meaning aware of) the existence of your controller classes.
Edit
I just re-read the last part of your question, about having multiple table controllers needing to listen for notification of the data being finished loading. For that, I suggest you use the Observer pattern in Cocoa by using NSNotificationCenter. You use use the notification center in the data model to send notifications to observers (you don't care who is observing; the notification center handles those details) and you'd also use it in your table controllers to subscribe to the notification. Delegates are a nice, simple solution if you only need one object to be directly called when something happens. Notifications are more complex and have more overhead, but give you the flexibility to have an arbitrary number of objects "listening" for a notification to be posted.
Follow-Up Response
A class doesn't define an informal protocol; the developer does. You could also define a formal protocol in a separate .h file and have the controller implement it if you want an enforceable contract. With a formal protocol, you can also use #optional on methods that don't have to be implemented by a class conforming to the protocol.
It is also not at all bad to instantiate the data model from within the table view controller. In fact, this is one very correct way to do it. Since the data model exists to encapsulate data that (presumably) a controller will want to display later, you can think of the controller as owning the data model. You may even consider making an instance variable (and perhaps a property, too) to store your data model. Besides that, your rewritten code looks good to me!
Do you mean the keyword self?
-(void)canHazCheeseburger:(BOOL)canHaz
{
if (canHaz) {
self.cheeseBurger = [[[CheeseBurger alloc] init] autorelease];
[cheeseBurger onNomNom];
}
}