[Openmcl-devel] cocoa memory management best practices?

Gary Byers gb at clozure.com
Fri Sep 24 13:42:59 PDT 2010

On Fri, 24 Sep 2010, Raffael Cavallaro wrote:

> I think it would be useful to know the best way to deal with the life cycle of cocoa objects. Possibly the various situations can be enumerated and memory management best practices outlined for each.
> Most importantly, please correct anything here that is wrong (i.e., likely to lead to a memory leak or outright crash), and please add any common situations that I've overlooked.
> 1. containing lisp class (i.e., not having  ns:ns-object as its metaclass) with an objective-c slot.
> 	- call ccl::terminate-when-unreachable on the containing lisp instance in its intitialize-instance method
> 	- do necessary deallocation (#/release, etc.) of objective-c slots in a ccl:terminate method on the containing lisp class

In general, whether or not there's a lisp reference to an ObjC object or not
currently has nothing to do with the lifetime of that ObjC object.  You might
want/need to declare that it does - to say that "as long as this particular
reference to the ObjC object exists, the ObjC object should be retained", in
which case:

(defclass lisp-object ()
   ((objc-slot ....)))

and maybe:

(defmethod initialize-instance :after ((obj lisp-object) &key)
   (with-slots (objc-slot) obj
     (when objc-slot
       (#/retain objc-slot)
       (terminate-when-unreachable obj))))

(defmethod terminate ((thing lisp-object))
   (let* ((objc-object (slot-value thing 'objc-slot)))
      (when objc-object
        (#/release objc-object))))

Unless it's really important to enforce that kind of assertion (that the
lifetime of the ObjC object can't be shorter than the lifetime of a particular
lisp reference to it), this may be overkill.

One example of this situation is just:

(defvar *w* (make-instance ns:ns-window ...))

Windows are ordinarily released when closed, so after that window's closed
*w* is "a pointer to some foreign address that used to contain an NSWindow;
who knows what - if anything - that address contains now."  That's not
the sort of thing that happens to first-class lisp objects - it's a little
like what happens if a DYNAMIC-EXTENT declaration is violated.  There are
at least two points of view here:

  - something (the bridge, maybe) should do something involving #/retain and
    #/release and termination to preserve the illusion that ObjC objects (like
    lisp objects in general) have indefinite extent (i.e., exist as long as
    it's possible to reference them.)
  - people should recoginize that ObjC objects have finite extent (may cease
    to meaningfully exist even though there are lisp references to them)

I think that the second of these things more accurately reflects the status
quo, so when I hear about cases where people would want to use termination
to deal with lisp objects that reference ObjC objects I'm skeptical of the
need to do that.

> 2. containing Objetive-c class (with ns:ns-object as its metaclass) with a:
> 2a. lisp slot
> 	- assume this lisp slot is taken care of by the gc?

The way that this is currently handled (prior to a month or so ago, it wasn't
really handled at all) is that the bridge defines a #/dealloc method for each
class that defines lisp slots (when no superclass does.)  If that automatically
generated  #/dealloc method is overridden, then the method should call
(OBJC:REMOVE-LISP-SLOTS instance), after it's dealt with those lisp slots and
before calling the next method.  That seems to work fairly well, though it's
new and the constraint on #/dealloc methods needs to be written down.

> 2b. objective-c slot
> 	option 1:
> 	- call ccl::terminate-when-unreachable on the containing objective-c instance in its intitialize-instance method
> 	- do necessary deallocation (#/release, etc.) of objective-c slots in a ccl:terminate method on the containing objective-c class
> 	option 2:
> 	- do necessary deallocation of objective-c slots in the containing objective-c class's #/dealloc method.
> 	option 3 ?:
> 	- for  objective-c objects owned/managed by a custom window class, do necessary deallocation in the custom window's #/close method

I vote for option 2 (using a #/dealloc method.)  Note that (in the
trunk for the last month or so and as mentioned above) it may be
necesary to call OBJC:REMOVE-LISP-SLOTS in a #/dealloc method; it's
harmless to call it if you aren't sure.

I sort of wish that the burden of freeing (REMHASHing) an instance's
lisp slots wasn't somehow left up to the user, but haven't come up
with a good way to avoid that.

> 3. Shared objective-c resources
> 	- put the shared resource into a special var and let it live for the duration of the program (it's memory will be reclaimed when the app quits)
> comments and corrections most welcome.
> warmest regards,
> Ralph
> Raffael Cavallaro
> raffaelcavallaro at me.com
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list