Let’s try to clear away some of the irrelevant stuff:On Dec 26, 2014, at 11:54 , N!K <email@hidden> wrote:
1) I added:
-(id)init { self = [ super init ]; if( self ){ NSLog(@"\n\n init"); } return self; }
-(id)initWithCoder:(NSCoder*)coder { self = [ super initWithCoder:coder ]; if( self ) NSLog(@"\n\n initWithCoder"); return self; }
Only initWithCoder: is reached; NSLog outputs from there and a breakpoint stops there.
init and initWithFrame: are not reached. No NSLog outputs. No breakpoint stops there.
—This raises a new question: Why isn’t ’init’ reached at all?—
This is a Cocoa matter. Every class can have multiple initializers (that is, instance methods whose name starts with ‘init’). One or more of these initializers is a “designated initializer�, that is, an initializer through which control must pass when the object is created. If there are more than one, control must pass through one and only one of them.
The designated initializer is an informal concept, so it has no syntactic marker. (In Obj-C — it does in Swift.) Also, a particular method name might be a designated initializer in one class, but not in another class. (A non-designated initializer is called a “convenience� initializer, and by the above rules any convenience initializer must invoke a designated initializer.) In particular, the designated initializer(s) of a class may not be the same as it superclass. The waters are muddied a bit more because initializers can be inherited. That makes is possible that the designated initializer depends not only of the class the method is defined in, but also the class of the object being initialized.
It’s all a bit of a mess, but it works quite well in practice so long as you don’t obsess about the informality of it all.
So, ‘init’ is the designated initializer of NSObject, but not of NSView. NSView’s designated initializer is ‘initWithFrame:’. If you wrote code to send ‘init’ to a NSView object, I’d expect you’d end up at ‘initWithFrame:’ — in class NSView, ‘init’ is a convenience initializer.
‘initWithCoder:’ is implemented in many classes, but you don’t know in general whether it’s a[nother] designated initializer or a convenience initializer. IOW, you don’t know, in terms of class machinery, whether -[NSView initWithCoder:] is supposed to invoke -[NSView initWithFrame:] or not. However, in this case, the resource loading guide suggests that one or other is invoked, but not both, so I guess they’re both designated initializers.
I need to know why initWithFrame stopped working, how to use initWithCoder: correctly, and what potential pitfalls are out there. There may be more than this one: WANNABEGEEK:“ Identity Inspector - User Defined Runtime Attributes In your implementation class you cannot use initWithCoder: otherwise your key-value path setting will not be picked up. You will have to do all your implementation within awakeFromNib.� Clearly I have some studying to pursue, after this fine start you have given me.
Be careful before putting your faith in information that comes from the Internet. It may have been true once but false now. It may be somewhat useful and technically inaccurate at the same time. This particular gem seems like an example of the latter. 4) Apparently initWith Coder: is suitable. It worked. But I don’t yet know why. I don’t even know what the “coder" argument refers to, or what it should be. In looking up initWith Coder: I found it described in Stack Overflow as The NSCoder class is used to archive/unarchive (marshal/unmarshal, serialize/deserialize) of objects.
This is a method to write objects on streams (like files, sockets) and being able to retrieve them later or in a different place.
I would suggest you to read Archiving
Since I didn’t plan to do any archiving at this point, I rejected it. Thanks for redirecting me.
*You* don’t do any unarchiving of views, but your app does, because it’s loading a view hierarchy from a NIB file, which is a file full of archived objects including your views. That’s *why* initWithCoder: is invoked, in general.
If you’re still with me, you’ll see (I hope) that everything appears to be working as it should, *except* that initWithCoder: is being invoked in a situation where we’d expect to see initWithFrame:.
None of use who have weighed into this thread have an explanation (yet) of why that is. We expect the NIB-loading machinery to produce an invocation of initWithFrame:, but it apparently isn’t. I just looked at the documentation again:
and I notice it says this (edited down a bit):
• By default, objects receive an initWithCoder: message. […]
• Custom views in OS X receive an initWithFrame: message. Custom views are subclasses of NSView for which Xcode does not have an available implementation. […]
When it encounters a custom view, Xcode encodes a special NSCustomView object into your nib file.
It’s certainly possible that in Xcode 6 and/or Yosemite, it's is a bit smarter about knowing when it has “an available implementation�. It’s certainly possible that it knows it has your custom view class in the project, and so optimizes this so that it’s treated as a *known* view, therefore sending the normal initWithCoder: instead of initWithFrame:. If that’s so, there’s nothing wrong here, and our collective wisdom was just a bit out of date.
Or, there’s something a bit more subtle going on, which I don’t know how to diagnose except by submitting a bug report (“my custom view gets the wrong initializer invoked�) and see what comes back.
Finally, the continuation of the above documentation reads as follows:
The custom view object includes the information it needs to build the real view subclass you specified. At load time, the NSCustomView object sends an alloc and initWithFrame: message to the real view class and then swaps the resulting view object in for itself. The net effect is that the real view object handles subsequent interactions during the nib-loading process.
If you think about it, this makes sense in terms of initializers. The NSCustomView placeholder object is in fact unarchived from the NIB file, so it gets initWithCoder:. The replacement object of your class is created directly, so it gets initWithFrame:.
|