[Openmcl-devel] object classes and nib files (a clue and a puzzle)
Dan Knapp
dankna at accela.net
Mon Jan 3 15:16:33 PST 2005
Hmm, I see what you're saying. In that case, I need to fix the docs,
which means I
need to understand this better...
> - (id)initWithWindowNibName:(NSString *)windowNibName
> Returns an NSWindowController object initialized with windowNibName,
> the name of the nib file (minus the “.nib” extension) that archives
> the receiver’s window. The windowNibName argument cannot be nil. Sets
> the owner of the nib file to the receiver. The default initialization
> turns on cascading, sets the shouldCloseDocument flag to NO, and sets
> the autosave name for the window’s frame to an empty string.
Hmmm. Yeah, that implies it would work without :owner.... I wonder
why my code
didn't, then. I guess I need to go resurrect my old test case and find
out...
> which seems to suggest that you are allowed to drop the :owner. I've
> also been digging into how nib files work (see below) and I see that
> initWithWindowNibName
> doesn't normal invoke an initialization callback so any lisp side
> initialization is going to have to be done by make-instance or the
> 'alloc method. Maybe I'm missing something but I can't see why it
> wouldn't work (and it does seem to work, since I've been doing lots of
> coding this way).
Well, there *is* a Lisp wrapper object, and it is constructed at some
point; otherwise you
wouldn't be able to refer to the object at all. The question is just
why it isn't getting initialized.
With the standard-class metaclass, it would be the default method on
initialize-instance that
actually performed the initialization of the Lisp slots, and I would be
very surprised were that
not also the case with objc wrapper metaclasses. Maybe do (trace
initialize-instance) to
see whether it's getting called? (That could generate a very high
volume of debug output,
of course...)
> Thinking about this did help me understand what was going on, however.
>
> With objc classes, the objc part gets initialized by the AppKit code,
> but the lisp part has to be initialized by ALLOCATE-INSTANCE. Normally
> this happens transparently when MAKE-INSTANCE calls ALLOCATE-INSTANCE
> and then sends the 'alloc and 'init messages to AppKit to initialize
> the other side.
Actually, no. Allocate-instance just reserves storage for the slots;
initialize-instance gives
them their bindings and values. The standard-class make-instance
simply invokes
allocate-instance and then initialize-instance; of course it's a little
more complicated with
objc classes, on account of the need to parse the arguments.
> Loading instances from a nib file does not invoke MAKE-INSTANCE, so
> ALLOCATE-INSTANCE is not invoked either. What you get is a lisp object
> that correctly maps to a valid OBJC object but has no lisp guts.
Hmm, yeah. Except with initialize-instance instead of
allocate-instance. Of course the
question then becomes: What values should the Lisp slots have? This
is not, after all,
a new object. Remember that a nib isn't necessarily constructed with
Interface Builder;
it can also be a snapshot of a running program. In the latter case,
it's quite likely that,
prior to being saved in the nib, the object had values which were not
the initial ones. So,
I don't think it would be correct to automatically call
initialize-instance on all
newly-awoken objects.
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.
> This works fine for pure OBJC classes because the lisp slots don't
> really exist anyway and accessing a slot gets magically turned into an
> AppKit call. If a class has real lisp slots (not :foreign-type
> definitions), however, then you end up with something that looks like
> a class, receives OBJC messages but generates errors if the lisp slots
> are accessed because SLOT-BOUNDP can't locate the slot definition.
>
> My hack was to make %OBJC-DOMAIN-SLOTS-VECTOR do slot vector
> initialization on demand.
The clean solution, I think, would be to make use of the objc
awakeFromNib message.
This is sent immediately upon loading of the object, and yes, Lisp is
quite able to receive
it. There ought to be, in Lisp, a default method on awake-from-nib
which would call
allocate-instance. It should *not* call initialize-instance; any such
behavior, if desired,
should be defined by the author of the specific class, with another
method on
awake-from-nib.
That would probably be a very small piece of code. The main thing
I'm not sure about
is how to define it as a default to be used everywhere it's
appropriate. Randy? Any
advice?
> This doesn't really fix the problem, though, because
> INITIALIZE-INSTANCE doesn't get called, either.
As I said above, I don't think it's appropriate to call
initialize-instance by default. If you
wanted to call it for your class, you would just do:
(define-objc-method ((:void awake-from-nib) alex-class)
(initialize-instance self))
For a short-term solution, this is also where you should call
(allocate-instance); in the
solution I suggest, you would instead begin this with
(call-next-method), and then the
default method would call (allocate-instance). I'm not sure, though,
whether you can
safely do this or whether it will try to allocate and initialize the
foreign slots as well.
If you try it, tell me if it works... I gather that you have just read
through the code for
allocate-instance, so perhaps you can answer that better than I can.
> I think that the right thing to do would be to call ALLOCATE-INSTANCE
> and INITIALIZE-INSTANCE whenever we try to access and unitialized OBJC
> instance, but I'm not clear on where the test would go.
That's probably a much more difficult test to make, and in what sense
is it uninitialized?
You'd have to do a heuristic check, since there's no general way to
tell; also, it would be
a performance hit at every access. There is no such behavior for
native Lisp objects;
you're quite free, as I read the standard, to call allocate-instance at
one point and then,
much later, call initialize-instance. The behavior if you tried to
access the slots in
between would be undefined, but, in practice, would just be an
unbound-slot error. So
Lisp isn't necessarily set up to make a test like that possible.
Anyway, it's not necessary
because you can use awake-from-nib.
Hmmm. I understand what's going on now; thank you. Before I correct
the docs, I'd like
to either have a best-practice to recommend, or see the problem fixed
entirely... Hrm.
-- Dan Knapp
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: text/enriched
Size: 7961 bytes
Desc: not available
URL: <https://lists.clozure.com/pipermail/openmcl-devel/attachments/20050103/74d358af/attachment.bin>
More information about the Openmcl-devel
mailing list