[Openmcl-devel] Objc Mem Management Question

Paul Krueger plkrueger at comcast.net
Tue Feb 5 09:54:45 PST 2013

Section 14.5.2 of the CCL doc which talks about defining Objc subclasses with lisp slots says in part:

"As one might expect, this has memory-management implications: we have to maintain an association between a MACPTR and a set of lisp objects (its slots) as long as the Objective-C instance exists, and we have to ensure that the Objective-C instance exists (does not have its -dealloc method called) while lisp is trying to think of it as a first-class object that can't be "deallocated" while it's still possible to reference it. Associating one or more lisp objects with a foreign instance is something that's often very useful; if you were to do this "by hand", you'd have to face many of the same memory-management issues."

I've always depended on this behavior and never had any problems. Recently I ran into a problem when I defined something that was morally equivalent to the following test case:

(defclass thing (ns:ns-view)
  ((slot :accessor slot
         :initform nil))
  (:metaclass ns:+ns-object))

(defmethod initialize-instance :after ((self thing)
  (ccl:terminate-when-unreachable self))

(defmethod ccl:terminate ((self thing))
  (format t "terminate called for thing: ~s" self))

(defun test-thing ()
  (let ((v (make-instance 'ns:ns-view))
        (th (make-instance 'thing)))
    (#/addSubview: v th)

Then in the listener I do the following:

? (setf v (test-thing))
#<NS-VIEW <NSView: 0x28379140> (#x28379140)>
? (gc)

And the message from the terminate method is displayed in the AltConsole app window. 

I'm trying to piece together how this all works without spending a lot of time looking through gc code, so I'd appreciate corrections to the following:

1. When we create an instance of thing a macptr is created that points to the ObjC instance and some sort of association is kept between that macptr and the lisp slot associated with that instance of thing. The reference count of the thing instance at that point is 1.

2. When the thing view is added to another view, its reference count is incremented by the parent view, so it is now 2. In theory I should be able to safely #/release the thing instance at this point although I did not actually do so in this example.

3. Since there is no longer any other lisp reference to the macptr it becomes a candidate to be gc'ed.

Now what I sort of expected at that point is that gc code would check the ref count of the object referred to by the macptr and if it is > 0 it would NOT gc the associated lisp slots and NOT call terminate for the thing instance. I've never before encountered any problems with the first assumption (e.g. lisp slots defined for custom Objective-C subclass instances always seem to hang around as long as the object is not dealloc'ed, without any concern to keep another reference to it from the lisp world). So is it possible that terminate is being called prematurely (before the ref count check is made) or is my understanding / expectation flawed and I've just been very lucky?

Note also that if I DO keep around another Lisp reference to that thing instance, then the terminate method is not called, as you would expect.

More information about the Openmcl-devel mailing list