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

Dan Knapp dankna at accela.net
Tue Jan 4 02:12:01 UTC 2005


> Lisp classes are all subclasses of OBJC classes and if you go through 
> the trouble of creating
> a custom lisp class, you are probably going to instance it as well. 
> Since the lisp class is created
> first, the OBJC component is always mapped to an existing object, and 
> everything works as
> advertised. The first exception to this rule would be when people 
> started using nib files, but all
> the nib examples are pure OBJC classes that simply get a list wrapper.
>
> In my case, I'm actually creating lisp classes from inside of OBJC 
> code. Who would have thought of that?
> MAKE-INSTANCE isn't called because lisp doesn't even know that the 
> instance exists until it's
> referenced - and then we reasonably assume that if we have a reference 
> to an instance that
> it's been created (how else would we have a reference?)

   Yeah, I agree, you've understood this correctly.

> 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.

   I was thinking of it as analogous to loading a saved Lisp image, 
which is supposed to
be able to resume from exactly where it left off, without the saved 
objects having to even
be aware of any interruption.  But that was a silly analogy.  So yeah, 
it's not a real issue
and we should probably just call initialize-instance by default.

> In any case, it's even worse then that. There is no support for method 
> qualifiers with OBJC messages,
> so you can't easily create an awakeFromNib method that wouldn't 
> override/be overridden
> by code put in by the programmer. (you could add gross hooks to 
> %pascal-functions%....yuk).
> Remember that awakeFromNib must be defined as a proper objc-method or 
> it won't be called
> at all (loadNibFile checks before calling. How it checks, I have no 
> idea, but it does).
>
> 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?

   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.

> 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).

> calling INITIALIZE-INSTANCE twice doesn't seem to do any damage 
> besides wasted
> cycles, but it's still wrong.

   Agreed.  It would also lead to unexpected bugs when the 
initialization has external
side-effects, such as inserting the object into a global list of all 
objects of that type.
Any such bugs are easy to code around, but potentially pretty 
confusing...

>>   The nib system does actually have hooks to allow it to be extended, 
>> so in principle it
>> would be possible to implement something that did save and restore 
>> Lisp slots.  This
>> would only be a very-long-term solution (nobody has time to develop 
>> it right now), and
>> I'm not sure it would be desirable, but I'm noting the possibility in 
>> case I'm the only one
>> who's thought of it.
>
> You're not. It would be very cool to create lispy palettes and create 
> pre-initialized lisp
> objects in interface builder, but I don't think that it's going to 
> happen because we would
> need to link interface builder to the lisp world somehow.

   I hadn't thought of that.  Too bad.  Anyway, this is pipe-dream 
territory; certainly there's
no clear need for such a feature.

> It's conceivable that one could write an OBJC library that would open 
> a listener connection
> to a running lisp process and be able to evaluate lisp expressions but 
> I think that in the
> end it would be so confusing that no one would actually use it :-)

   Agreed!  Like all the advanced features of asdf...

> What is doable is writing palettes in OBJC that create lisp objects 
> with initialized :foreign slots
> with matching lisp modules to do the lisp side initialization. I 
> haven't actually written a palette
> yet, but it's supposed to be pretty straightforward.

   Yeah, I have, it is.

-- Dan Knapp




More information about the Openmcl-devel mailing list