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

alex crain alexcrain at mail.widgetworks.com
Mon Jan 3 09:11:19 PST 2005

On Jan 2, 2005, at 6:03 PM, Dan Knapp wrote:

>> I'm having trouble loading an OBJC class from a nib file.
>> I have a class HemlockTextView that inherits NSTextView and I do
>> ? (make-instance 'ns:ns-window-controller :with-window-nib-name 
>> #@"test")
>   There is an example of how to do this at the end of:
> 10.2. Instantiating Objective-C Objects
> http://www.openmcl.com/Doc/ch10s02.html
>   NSWindowController is something of a special case.  It requires the 
> :owner
> parameter.  The object passed as the :owner should be an allocated but
> uninitialized instance of the same class as the one defined in the NIB 
> file as
> the owner.  It is then initialized to be identical to the saved object.
>   Things do appear to work if you leave :owner off, but it's behaviour 
> undefined
> by Apple.

I found this:

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

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

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

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.

     ;; pulled from ALLOCATE-iNSTANCE
     (defun allocate-slot-vector-and-register-instance (instance class)
       (let* ((raw-ptr (raw-macptr-for-instance instance))
	     (slot-vector (create-foreign-instance-slot-vector class)))
	(when slot-vector
	  (setf (slot-vector.instance slot-vector) raw-ptr)
	  (setf (gethash raw-ptr *objc-object-slot-vectors*) slot-vector))
	(register-canonical-objc-instance instance raw-ptr)))

     (defun %objc-domain-slots-vector (p)
       (let* ((type (%macptr-type p))
	     (flags (ldb objc-type-flags type))
	     (index (ldb objc-type-index type)))
	(declare (fixnum type flags index))
	(ecase flags
	  (#.objc-flag-instance (or (gethash p *objc-object-slot-vectors*)
				    ; try to allocate the slot vector on demand
				    (allocate-slot-vector-and-register-instance p (class-of p))
				    (error "~s has no slots." p)))
	  (#.objc-flag-class (id->objc-class-slots-vector index))
	  (#.objc-flag-metaclass (id->objc-metaclass-slots-vector index)))))

This doesn't really fix the problem, though, because 
INITIALIZE-INSTANCE doesn't get called, either.

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.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: text/enriched
Size: 5248 bytes
Desc: not available
URL: <https://lists.clozure.com/pipermail/openmcl-devel/attachments/20050103/f73da85a/attachment.bin>

More information about the Openmcl-devel mailing list