initWithFrame not being called on my NSImageView subclass
-
I've written a subclass of NSImageView, and my intent was to set some
sane default values for some member vars in my initWithFrame:
( NSRect ) method.
However... initWithFrame simply isn't being called. I know the
instance is being instantiated ( it's the main view for a document
based app ) and messages are going to/from the instance. All my
bindings work, for example. The trouble is, initWithFrame is never
called and as such, my member vars are all set to zero, and thus some
things are wonky.
In principle, I could probably use awakeFromNib and the like, but I'm
concerned that initWithFrame isn't being called.
Anybody know why it wouldn't be called? It doesn't appear to be
misspelled.
For reference, this is a document based app. 10.5.2, using the 10.5
SDK. No GC.
<shamyl...>
"Our response to being bored and rich is not to discard
possessions and live more simply, but to buy more stuff
to reduce the space in which we might contemplate
our shame." -
On Thu, Feb 21, 2008 at 1:36 PM, Shamyl Zakariya <shamyl...> wrote:> Anybody know why it wouldn't be called? It doesn't appear to be
> misspelled.
Re-read the documentation here:
http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaViewsGuide/S
ubclassingNSView/chapter_6_section_2.html
Short version: subclasses of views that you've put in your nib have
already been instantiated when they were freeze-dried into the nib.
It would be incorrect to call -initWithFrame: again. That's why you
have -awakeFromNib.
--Kyle Sluder -
If the view's being loaded from a nib it's archived, so you need -
initWithCoder:
Mike.
On 21 Feb 2008, at 18:36, Shamyl Zakariya wrote:> I've written a subclass of NSImageView, and my intent was to set
> some sane default values for some member vars in my initWithFrame:
> ( NSRect ) method.
>
> However... initWithFrame simply isn't being called. I know the
> instance is being instantiated ( it's the main view for a document
> based app ) and messages are going to/from the instance. All my
> bindings work, for example. The trouble is, initWithFrame is never
> called and as such, my member vars are all set to zero, and thus
> some things are wonky.
>
> In principle, I could probably use awakeFromNib and the like, but
> I'm concerned that initWithFrame isn't being called.
>
> Anybody know why it wouldn't be called? It doesn't appear to be
> misspelled.
>
> For reference, this is a document based app. 10.5.2, using the 10.5
> SDK. No GC.
>
> <shamyl...>
> "Our response to being bored and rich is not to discard
> possessions and live more simply, but to buy more stuff
> to reduce the space in which we might contemplate
> our shame." -
On Thu, Feb 21, 2008 at 2:06 PM, Mike Abdullah
<cocoadev...> wrote:> If the view's being loaded from a nib it's archived, so you need -
> initWithCoder:
I'm not sure -initWithCoder: is guaranteed to be called, and is
certainly not the "proper" place to be performing object
initialization after awaking from a nib. The official recommendation
is to either create an IB plugin so that your version of
-initWithFrame: is called at design time, or perform subclass-specific
initialization in -awakeFromNib.
http://lists.apple.com/archives/cocoa-dev/2002/Jul/msg01647.html
--Kyle Sluder -
That makes sense, thank you.
<shamyl...>
"Such a theory has to be bizarre and elaborate, as well as being
stupid"
-- Jim Loy, regarding a hollow earth
On Feb 21, 2008, at 2:00 PM, Kyle Sluder wrote:> On Thu, Feb 21, 2008 at 1:36 PM, Shamyl Zakariya
> <shamyl...> wrote:
>> Anybody know why it wouldn't be called? It doesn't appear to be
>> misspelled.
>
> Re-read the documentation here:
> http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaViewsGuide/S
ubclassingNSView/chapter_6_section_2.html
>
> Short version: subclasses of views that you've put in your nib have
> already been instantiated when they were freeze-dried into the nib.
> It would be incorrect to call -initWithFrame: again. That's why you
> have -awakeFromNib.
>
> --Kyle Sluder -
Wonderful! Now I have sane defaults and my controls bound to them have
the correct values.
Thanks for your help.
<shamyl...>
"finite=alright"
--David Byrne
On Feb 21, 2008, at 2:06 PM, Mike Abdullah wrote:> If the view's being loaded from a nib it's archived, so you need -
> initWithCoder:
>
> Mike.
>
> On 21 Feb 2008, at 18:36, Shamyl Zakariya wrote:
>
>> I've written a subclass of NSImageView, and my intent was to set
>> some sane default values for some member vars in my initWithFrame:
>> ( NSRect ) method.
>>
>> However... initWithFrame simply isn't being called. I know the
>> instance is being instantiated ( it's the main view for a document
>> based app ) and messages are going to/from the instance. All my
>> bindings work, for example. The trouble is, initWithFrame is never
>> called and as such, my member vars are all set to zero, and thus
>> some things are wonky.
>>
>> In principle, I could probably use awakeFromNib and the like, but
>> I'm concerned that initWithFrame isn't being called.
>>
>> Anybody know why it wouldn't be called? It doesn't appear to be
>> misspelled.
>>
>> For reference, this is a document based app. 10.5.2, using the 10.5
>> SDK. No GC.
>>
>> <shamyl...>
>> "Our response to being bored and rich is not to discard
>> possessions and live more simply, but to buy more stuff
>> to reduce the space in which we might contemplate
>> our shame."
> -
OK, sounds like I'm not out of the woods.
Here's the trouble: I have some controls ( in my toolbar ) controlling
a couple properties of the image view subclass. Specifically, a zoom
factor and a tiling mode ( this is just a quick-dirty app for viewing
compressed DDS textures. I've already written a quicklook plugin and
an NSCustomBitmapImageRep so the plumbing's all there ).
When I assign a default tiling mode and zoom factor in awakeFromNib,
for some reason it doesn't "propagate" to the bindings.
NSLogging the order of events, I see something like this:
1) new document is opened from a DDS file:
2) TilingImageView is asked for a scaling factor. _scale is zero so it
returns zero
3) awakeFromNib is called. I call setScale: 1
4) Nothing. My slider's still showing zero.
Now, my slider does work: Dragging it changes the scaling factor. But
for some reason calling [self setScale: 1] in awakeFromNib doesn't
notify the slider in my toolbar.
This is why I'm trying to find a way to make certain _scale = 1 before
bindings queries my image view instance.
<shamyl...>
"In the same trial, only 16 percent of those who received placebo
sandwiches reported experiencing high levels of deliciousness."
--The Onion
On Feb 21, 2008, at 2:15 PM, Kyle Sluder wrote:> On Thu, Feb 21, 2008 at 2:06 PM, Mike Abdullah
> <cocoadev...> wrote:
>> If the view's being loaded from a nib it's archived, so you need -
>> initWithCoder:
>
> I'm not sure -initWithCoder: is guaranteed to be called, and is
> certainly not the "proper" place to be performing object
> initialization after awaking from a nib. The official recommendation
> is to either create an IB plugin so that your version of
> -initWithFrame: is called at design time, or perform subclass-specific
> initialization in -awakeFromNib.
>
> http://lists.apple.com/archives/cocoa-dev/2002/Jul/msg01647.html
>
> --Kyle Sluder -
Well, to finish this off, I just made some calls to initialize from
windowControllerDidLoadNib and averything's peachy, and no dependancy
on initWithCoder.
Shamyl Zakariya
- The fantabulous contrapulation of professor Horatio Huffnagel
On Feb 21, 2008, at 2:30 PM, Shamyl Zakariya wrote:> OK, sounds like I'm not out of the woods.
>
> Here's the trouble: I have some controls ( in my toolbar )
> controlling a couple properties of the image view subclass.
> Specifically, a zoom factor and a tiling mode ( this is just a quick-
> dirty app for viewing compressed DDS textures. I've already written
> a quicklook plugin and an NSCustomBitmapImageRep so the plumbing's
> all there ).
>
> When I assign a default tiling mode and zoom factor in awakeFromNib,
> for some reason it doesn't "propagate" to the bindings.
>
> NSLogging the order of events, I see something like this:
>
> 1) new document is opened from a DDS file:
> 2) TilingImageView is asked for a scaling factor. _scale is zero so
> it returns zero
> 3) awakeFromNib is called. I call setScale: 1
> 4) Nothing. My slider's still showing zero.
>
> Now, my slider does work: Dragging it changes the scaling factor.
> But for some reason calling [self setScale: 1] in awakeFromNib
> doesn't notify the slider in my toolbar.
>
> This is why I'm trying to find a way to make certain _scale = 1
> before bindings queries my image view instance.
>
> <shamyl...>
> "In the same trial, only 16 percent of those who received placebo
> sandwiches reported experiencing high levels of deliciousness."
> --The Onion
>
>
> On Feb 21, 2008, at 2:15 PM, Kyle Sluder wrote:
>
>> On Thu, Feb 21, 2008 at 2:06 PM, Mike Abdullah
>> <cocoadev...> wrote:
>>> If the view's being loaded from a nib it's archived, so you need -
>>> initWithCoder:
>>
>> I'm not sure -initWithCoder: is guaranteed to be called, and is
>> certainly not the "proper" place to be performing object
>> initialization after awaking from a nib. The official recommendation
>> is to either create an IB plugin so that your version of
>> -initWithFrame: is called at design time, or perform subclass-
>> specific
>> initialization in -awakeFromNib.
>>
>> http://lists.apple.com/archives/cocoa-dev/2002/Jul/msg01647.html
>>
>> --Kyle Sluder -
> I'm not sure -initWithCoder: is guaranteed to be called, and is
> certainly not the "proper" place to be performing object
> initialization after awaking from a nib.
What's guaranteed is that all init methods on a class will funnel into
one of the designated init methods. A subclasser should always
override all designated initializers of the superclass (if he needs to
do work at initialization time). Then you're guaranteed to have your
setup performed no matter how the object is created.
For NSView, -initWithFrame: and -initWithCoder: are the designated
initializers.
-awakeFromNib is good if the work you're doing involves other objects
from a nib. In particular, there's a subtlety with decoding from an
archive: suppose two objects refer to each other and encode each other
(with encodeObject:forKey:) in a nib. Well, one of them has to finish
initializing before the other. The upshot is that when you call
-decodeObject:forKey: in -initWithCoder:, you'll get back an object,
but it may not have finished its initialization yet. It's fine to
send messages defined at the NSObject level (like -retain) to it, but
not to treat it as a full fledged object of whatever class you know it
to be.
In -awakeFromNib you're guaranteed that all objects in the nib have
been through initialization had have their outlets hooked up.
-Ken
Cocoa Frameworks
On Thu, Feb 21, 2008 at 11:15 AM, Kyle Sluder
<kyle.sluder+<cocoa-dev...> wrote:> On Thu, Feb 21, 2008 at 2:06 PM, Mike Abdullah
> <cocoadev...> wrote:
>> If the view's being loaded from a nib it's archived, so you need -
>> initWithCoder:
>
> I'm not sure -initWithCoder: is guaranteed to be called, and is
> certainly not the "proper" place to be performing object
> initialization after awaking from a nib. The official recommendation
> is to either create an IB plugin so that your version of
> -initWithFrame: is called at design time, or perform subclass-specific
> initialization in -awakeFromNib.
>
> http://lists.apple.com/archives/cocoa-dev/2002/Jul/msg01647.html
>
> --Kyle Sluder
> -
On Thu, Feb 21, 2008 at 11:30 AM, Shamyl Zakariya <shamyl...> wrote:> OK, sounds like I'm not out of the woods.
>
> Here's the trouble: I have some controls ( in my toolbar ) controlling
> a couple properties of the image view subclass. Specifically, a zoom
> factor and a tiling mode ( this is just a quick-dirty app for viewing
> compressed DDS textures. I've already written a quicklook plugin and
> an NSCustomBitmapImageRep so the plumbing's all there ).
>
> When I assign a default tiling mode and zoom factor in awakeFromNib,
> for some reason it doesn't "propagate" to the bindings.
>
> NSLogging the order of events, I see something like this:
>
> 1) new document is opened from a DDS file:
> 2) TilingImageView is asked for a scaling factor. _scale is zero so it
> returns zero
> 3) awakeFromNib is called. I call setScale: 1
Are you calling setScale: on the model object, or on the view?
Calling a setter on a bound view object does not propagate the change
to the model.
It's basically exactly like pre-bindings models like delegates or
target-action. The model and the view are not completely aliased to
each other, but each updates the other under certain circumstances.
Typically the view updates the model in response to user actions like
a mouse click, not programmatic actions like calling a setter.
-Ken> 4) Nothing. My slider's still showing zero.
>
> Now, my slider does work: Dragging it changes the scaling factor. But
> for some reason calling [self setScale: 1] in awakeFromNib doesn't
> notify the slider in my toolbar.
>
> This is why I'm trying to find a way to make certain _scale = 1 before
> bindings queries my image view instance.
>
> <shamyl...>
> "In the same trial, only 16 percent of those who received placebo
> sandwiches reported experiencing high levels of deliciousness."
> --The Onion
>
>
>
>
> On Feb 21, 2008, at 2:15 PM, Kyle Sluder wrote:
>
>> On Thu, Feb 21, 2008 at 2:06 PM, Mike Abdullah
>> <cocoadev...> wrote:
>>> If the view's being loaded from a nib it's archived, so you need -
>>> initWithCoder:
>>
>> I'm not sure -initWithCoder: is guaranteed to be called, and is
>> certainly not the "proper" place to be performing object
>> initialization after awaking from a nib. The official recommendation
>> is to either create an IB plugin so that your version of
>> -initWithFrame: is called at design time, or perform subclass-specific
>> initialization in -awakeFromNib.
>>
>> http://lists.apple.com/archives/cocoa-dev/2002/Jul/msg01647.html
>>
>> --Kyle Sluder
> -
On Thu, Feb 21, 2008 at 2:41 PM, Ken Ferry <kenferry...> wrote:> What's guaranteed is that all init methods on a class will funnel into
> one of the designated init methods. A subclasser should always
> override all designated initializers of the superclass (if he needs to
> do work at initialization time). Then you're guaranteed to have your
> setup performed no matter how the object is created.
What I'm saying is that I'm not sure this is guaranteed to be true for
unarchiving. Since (with rare exception) any override of a designated
initializer is supposed to call super's implementation, it would be
very easy to introduce improper behavior by calling -[NSView
initWithFrame:] at design time, archiving the object graph into the
nib, and then calling -[MyView initWithFrame:], which then invokes
-[NSView initWithFrame:], when unarchiving the view from the nib.
I think the Apple docs are pretty clear that there is no guarantee
whatsoever that any initializer will be called on your objects when
unarchiving from a nib. The nib unarchiving mechanism doesn't really
care about your setters, getters, and initializers. It restores
whatever state it knew about when it archived the object, trusting you
to implement -awakeFromNib if you need subclass-specific processing.
--Kyle Sluder -
On Thu, Feb 21, 2008 at 1:46 PM, Kyle Sluder
<kyle.sluder+<cocoa-dev...> wrote:> On Thu, Feb 21, 2008 at 2:41 PM, Ken Ferry <kenferry...> wrote:
>> What's guaranteed is that all init methods on a class will funnel into
>> one of the designated init methods. A subclasser should always
>> override all designated initializers of the superclass (if he needs to
>> do work at initialization time). Then you're guaranteed to have your
>> setup performed no matter how the object is created.
>
> What I'm saying is that I'm not sure this is guaranteed to be true for
> unarchiving. Since (with rare exception) any override of a designated
> initializer is supposed to call super's implementation, it would be
> very easy to introduce improper behavior by calling -[NSView
> initWithFrame:] at design time, archiving the object graph into the
> nib, and then calling -[MyView initWithFrame:], which then invokes
> -[NSView initWithFrame:], when unarchiving the view from the nib.
>
> I think the Apple docs are pretty clear that there is no guarantee
> whatsoever that any initializer will be called on your objects when
> unarchiving from a nib. The nib unarchiving mechanism doesn't really
> care about your setters, getters, and initializers. It restores
> whatever state it knew about when it archived the object, trusting you
> to implement -awakeFromNib if you need subclass-specific processing.
Hm, sorry if the docs confused you.. if you can point at the docs that
made you think this, it'd be great to have a bug.
All objects _do_ go through some init method, including those in a
nib, and every init method of a class is intended to call through to a
designated initializer. You can count on this. -awakeFromNib is
great for setup that really does have to do with a nib, but pure
initial object initialization is better done in an init method.
Otherwise, you risk that setup not getting done if your object is
created in a way that doesn't involve a nib. For example,
NSCollectionView and NSToolbar with both sometimes try to 'copy' view
hierarchies by encoding and decoding to a keyed archive. (That kind
of copying is really hard to get right, by the way.)
As Ondra described, there is complexity in exactly which init method
is used for what object that you see in IB, but if you override all of
the designated initializers then your code won't care which init
method actually gets called. It might be any of -init,
-initWithCoder:, or -initWithFrame:, depending. IB will also show a
few objects that are actually external to the nib, like the nib file's
owner and the shared NSApplication object. These few objects already
existed and were initialized before the nib was instantiated, so they
won't get an init method when the nib is coming up.
Ken Ferry
Cocoa Frameworks -
On Feb 21, 2008, at 14:06, Ken Ferry wrote:> Hm, sorry if the docs confused you.. if you can point at the docs that
> made you think this, it'd be great to have a bug.
There's:
http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaViewsGuide/S
ubclassingNSView/chapter_6_section_2.html#/
/apple_ref/doc/uid/TP40002978-CH7-DontLinkElementID_12
It doesn't actually say that unarchived nib views *don't* go through
initWithCoder, but it doesn't promise that either.
Incidentally, that document was written pre-Leopard, and in IB 3
there's a third case. If you have a IB plugin that creates the view,
then the subclass initWithFrame *is* going to get called (from within
IB). At least I think so.


