diff --git a/include/wx/cocoa/ObjcRef.h b/include/wx/cocoa/ObjcRef.h index 33086d2ab8..44acfa8c45 100644 --- a/include/wx/cocoa/ObjcRef.h +++ b/include/wx/cocoa/ObjcRef.h @@ -15,6 +15,100 @@ // Reuse wxCFRef-related code (e.g. wxCFRetain/wxCFRelease) #include "wx/mac/corefoundation/cfref.h" +// NOTE WELL: We can only know which Objective-C runtime is being used when compiling Objective-C. +// Therefore we cannot implement these functions except when compiling Objective-C. +#ifdef __OBJC__ +/*! @function wxGCSafeRetain + @templatefield Type (implicit) An Objective-C class type + @arg r Pointer to Objective-C object. May be null. + @abstract Retains the Objective-C object, even when using Apple's garbage collector + @discussion + When Apple's garbage collector is enabled, the usual [obj retain] and [obj release] messages + are ignored. Instead the collector with help from compiler-generated write-barriers tracks + reachable objects. The write-barriers are generated when setting i-vars of C++ classes but + they are ignored by the garbage collector unless the C++ object is in GC-managed memory. + + The simple solution is to use CFRetain on the Objective-C object which has been enhanced in + GC mode to forcibly retain the object. In Retain/Release (RR) mode the CFRetain function has + the same effect as [obj retain]. Note that GC vs. RR is selected at runtime. + + Take care that wxGCSafeRetain must be balanced with wxGCSafeRelease and that conversely + wxGCSafeRelease must only be called on objects to balance wxGCSafeRetain. In particular when + receiving an Objective-C object from an alloc or copy method take care that you must retain + it with wxGCSafeRetain and balance the initial alloc with a standard release. + + Example: + wxGCSafeRelease(m_obj); // release current object (if any) + NSObject *obj = [[NSObject alloc] init]; + m_obj = wxGCSafeRetain(obj); + [obj release]; + + Alternatively (same effect, perhaps less clear): + wxGCSafeRelease(m_obj); // release current object (if any) + m_obj = wxGCSafeRetain([[NSObject alloc] init]); + [m_obj release]; // balance alloc + + Consider the effect on the retain count from each statement (alloc, CFRetain, release) + In RR mode: retainCount = 1, +1, -1 + In GC mode: strongRetainCount = 0, +1, -0 + + This is a template function to ensure it is used on raw pointers and never on pointer-holder + objects via implicit conversion operators. +*/ +template +inline Type * wxGCSafeRetain(Type *r) +{ +#ifdef __NEXT_RUNTIME__ + return static_cast(wxCFRetain(r)); +#else + return [r retain]; +#endif +} + +/*! @function wxGCSafeRelease + @templatefield Type (implicit) An Objective-C class type + @arg r Pointer to Objective-C object. May be null. + @abstract Balances wxGCSafeRetain. Particularly useful with the Apple Garbage Collector. + @discussion + See the wxGCSafeRetain documentation for more details. + + Example (from wxGCSafeRetain documentation): + wxGCSafeRelease(m_obj); // release current object (if any) + m_obj = wxGCSafeRetain([[NSObject alloc] init]); + [m_obj release]; // balance alloc + + When viewed from the start, m_obj ought to start as nil. However, the second time through + the wxGCSafeRelease call becomes critical as it releases the retain from the first time + through. + + In the destructor for this C++ object with the m_obj i-var you ought to do the following: + wxGCSafeRelease(m_obj); + m_obj = nil; // Not strictly needed, but safer. + + Under no circumstances should you balance an alloc or copy with a wxGCSafeRelease. +*/ +template +inline void wxGCSafeRelease(Type *r) +{ +#ifdef __NEXT_RUNTIME__ + wxCFRelease(r); +#else + [r release]; +#endif +} +#else +// NOTE: When not compiling Objective-C, declare these functions such that they can be +// used by other inline-implemented methods. Since those methods in turn will not actually +// be used from non-ObjC code there is no problem. + +template +inline Type * wxGCSafeRetain(Type *r); + +template +inline void wxGCSafeRelease(Type *r); + +#endif //def __OBJC__ + /* wxObjcAutoRefFromAlloc: construct a reference to an object that was [NSObject -alloc]'ed and thus does not need a retain @@ -65,7 +159,7 @@ public: // CFRetain // GC: Object is strongly retained and prevented from being collected // non-GC: Simply realizes it's an Objective-C object and calls [p retain] - wxCFRetain(p); + wxGCSafeRetain(p); // ObjcRelease (e.g. [p release]) // GC: Objective-C retain/release mean nothing in GC mode // non-GC: This is a normal release call, balancing the retain @@ -76,12 +170,12 @@ public: } wxObjcAutoRefFromAlloc(const wxObjcAutoRefFromAlloc& otherRef) : m_ptr(otherRef.m_ptr) - { wxCFRetain(m_ptr); } + { wxGCSafeRetain(m_ptr); } ~wxObjcAutoRefFromAlloc() - { wxCFRelease(m_ptr); } + { wxGCSafeRelease(m_ptr); } wxObjcAutoRefFromAlloc& operator=(const wxObjcAutoRefFromAlloc& otherRef) - { wxCFRetain(otherRef.m_ptr); - wxCFRelease(m_ptr); + { wxGCSafeRetain(otherRef.m_ptr); + wxGCSafeRelease(m_ptr); m_ptr = otherRef.m_ptr; return *this; } @@ -95,9 +189,10 @@ protected: The pointer to the Objective-C object is typed as void* to avoid compiler-generated write barriers as would be used for implicitly __strong object pointers and to avoid the similar read barriers as would be used for an explicitly __weak object pointer. The write barriers - are unsuitable because they assume the pointer (e.g. this object) is located in the heap - which we can't guarantee and in fact most often we are used as a global. We therefore - use the CFRetain/CFRelease functions which work regardless of our memory location. + are useless unless this object is located in GC-managed heap which is highly unlikely. + + Since we guarantee strong reference via CFRetain/CFRelease the write-barriers are not needed + at all, even if this object does happen to be allocated in GC-managed heap. */ void *m_ptr; };