[Openmcl-devel] object classes and nib files (a clue and a puzzle)

alex crain alexcrain at mail.widgetworks.com
Mon Jan 3 23:35:26 PST 2005

On Jan 3, 2005, at 9:12 PM, Dan Knapp wrote:

>> I'm not sure that this is a real issue because regardless of where 
>> nib files come from, there
>> is no practical way for a nib file to hold a lisp value. foreign-type 
>> slots are handled correctly
>> because it is assumed that initialization has already taken place on 
>> the OBJC side. We can
>> assume that lisp slots are always undefined because the nib file has 
>> no way of storing the
>> value if they wern't.
>   I wasn't so much thinking that it would already hold a Lisp value, 
> obviously not
> practical today, as that it might not be logically "new" so the 
> default values might not make
> sense.  I'm having trouble thinking of a useful example, though.  I 
> suppose as long as
> programmers are aware that the defaults are going to be set whenever 
> an object is
> loaded from a nib, it's easy to work around any difficulties that do 
> come up.

This is splitting hairs, but my point was the even if the OBJC fields 
are not logically new,
it's irrelevant because we're talking about INITIALIZE-INSTANCE which 
should only know about
the lisp side of things. The lisp side assumes that the OBJC has 
already correctly initialized
everything when it load the nib.

The one exception to this would be something like:

(defclass foo (ns:ns-object)
   ((bar :foreign_type :id :initform #@"asdf" :reader getbar)
   (:metaclass ns:+ns-object))

Which is pretty evil anyway because the programmer is invoking two 
distinct initialization
strategies without a clear protocol for execution. If I have a nib file 
which instantiates foo
and sets the bar to "qwerty", then the value of bar is inherently 
ambiguous. If would be better
to initialize bar with an :initWithWhatever message.

>> Also, awakeFromNib is called after initWithWhatever, IFF 
>> initWithWhatever is defined, and both
>> methods assume that ALLOCATE-INSTANCE has already been called. You 
>> could add a default
>> init handler to every class, but loadNibFile actually looks for the 
>> presence of the method to decide
>> whether to call it - it may end up calling the wrong method instead 
>> of what the programmer intended
>> simply because it's there.
>   Hmmmm.....  The docs seem to suggest that it's always initWithCoder? 
>  In which case
> the thing to do would be to call (allocate-instance) in 
> init-with-coder, and then
> (initialize-instance) in awake-from-nib, yes?  Or am I wrong?

Even initWithCoder assumes that the core initialization has already 
been done.

>   It doesn't strike me as a problem that there's no way to prevent the 
> programmer from
> causing trouble by forgetting to call (send-super).  It's not an undue 
> burden; it just has
> to be documented.

But thats just it - there is no superclass. If the programmer defined 
an :initWithWhatever
method then they will clobber the system default completely. You can't 
do something like
(define-objc-method ((:id init) ns:ns-object)
   (initialize-instance self)

because OBJC will simply ignore it and use the built in init method for 

Thus the rule becomes:

IF a class is loaded from a nib file AND there is no existing 
initWithWhatever method THEN
     add one

IF a class is loaded from a nib file AND there is an existing 
initWithWhatever methods THEN
     add INITIALIZE-INSTANCE to the initWithWhatever methods

IF the class accepts more then one distinct initWithWhatever method THEN
     add INITIALIZE_INSTANCE to any defined init methods
     create init methods for all other cases

I repeat. Yuk

>> What does work is
>> (define-objc-method ((:id :init-with-frame (:<NSR>ect frame))
>> 		     hemlock-text-view)
>>   (initialize-instance self)
>>   (send-super :init-with-frame frame)
>>   self)
>> but this also means that we call INITIALIZE-INSTANCE twice if the 
>> class is created with
>> (MAKE-INSTANCE 'myclass :with-frame frame)
>   The question to ask is, how does it know to call initWithFrame?  I'm 
> guessing that the
> initWithCoder method must be responsible for figuring that out; it 
> doesn't make sense
> that the generic nib-loading code would just know which method to call 
> for every possible
> type of object.  Whatever function figures out what initializer to 
> call, that's the one we
> want to override to call (initialize-instance), because that one will 
> only be called when
> loading from nibs and not from (make-instance).

When an object is unarchived, the initWithCoder method comes with it 
and supplies the
logic to unarchive any children of the object. In my case, I unarchive 
an instance of
ModelineScrollView which automatically invokes [newinstance 
initWithCoder]. I can't
override the initWithCoder method unless I know exactly what 
ModelineScrollView intended
to do in the first place.

Also, since initWithCoder is in the nib file, it exists as a piece of 
OBJC code and has no
way of invoking INITIALIZE-INSTANCE anyway.


More information about the Openmcl-devel mailing list