X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e76d459d94ca49f3b5568cdd4604b7dbb645fc2d..cbeda384e51acb82e13994cb67ac1714669cae10:/include/wx/cocoa/ObjcRef.h diff --git a/include/wx/cocoa/ObjcRef.h b/include/wx/cocoa/ObjcRef.h index faa11bea36..f08c6435dd 100644 --- a/include/wx/cocoa/ObjcRef.h +++ b/include/wx/cocoa/ObjcRef.h @@ -11,6 +11,107 @@ #ifndef _WX_COCOA_OBJCREF_H__ #define _WX_COCOA_OBJCREF_H__ + +// Reuse wxCFRef-related code (e.g. wxCFRetain/wxCFRelease) +#include "wx/osx/core/cfref.h" + +// NOTE WELL: We can only know whether or not GC can be 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 __OBJC_GC__ + 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 __OBJC_GC__ + 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 the compiler ought not emit them. If it emits an out of +// line copy of those methods then presumably it will have also emitted at least one +// out of line copy of these functions from at least one Objective-C++ translation unit. +// That means the out of line implementation will be available at link time. + +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 @@ -24,12 +125,32 @@ struct objc_object; class wxObjcAutoRefBase { protected: + /*! @function ObjcRetain + @abstract Simply does [p retain]. + */ static struct objc_object* ObjcRetain(struct objc_object*); + + /*! @function ObjcRelease + @abstract Simply does [p release]. + */ static void ObjcRelease(struct objc_object*); }; -// T should be a pointer like NSObject* +/*! @class wxObjcAutoRefFromAlloc + @templatefield T The type of _pointer_ (e.g. NSString*, NSRunLoop*) + @abstract Pointer-holder for Objective-C objects + @discussion + When constructing this object from a raw pointer, the pointer is assumed to have + come from an alloc-style method. That is, once you construct this object from + the pointer you must not balance your alloc with a call to release. + This class has been carefully designed to work with both the traditional Retain/Release + and the new Garbage Collected modes. In RR-mode it will prevent the object from being + released by managing the reference count using the retain/release semantics. In GC-mode + it will use a method (currently CFRetain/CFRelease) to ensure the object will never be + finalized until this object is destroyed. + */ + template class wxObjcAutoRefFromAlloc: wxObjcAutoRefBase { @@ -37,34 +158,70 @@ public: wxObjcAutoRefFromAlloc(T p = 0) : m_ptr(p) // NOTE: this is from alloc. Do NOT retain - {} + { + // 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] + 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 + ObjcRelease(static_cast(p)); + // The overall result: + // GC: Object is strongly retained + // non-GC: Retain count is the same as it was (retain then release) + } wxObjcAutoRefFromAlloc(const wxObjcAutoRefFromAlloc& otherRef) : m_ptr(otherRef.m_ptr) - { ObjcRetain(m_ptr); } + { wxGCSafeRetain(m_ptr); } ~wxObjcAutoRefFromAlloc() - { ObjcRelease(m_ptr); } + { wxGCSafeRelease(m_ptr); } wxObjcAutoRefFromAlloc& operator=(const wxObjcAutoRefFromAlloc& otherRef) - { ObjcRetain(otherRef.m_ptr); - ObjcRelease(m_ptr); + { wxGCSafeRetain(otherRef.m_ptr); + wxGCSafeRelease(m_ptr); m_ptr = otherRef.m_ptr; return *this; } operator T() const - { return m_ptr; } + { return static_cast(m_ptr); } T operator->() const - { return m_ptr; } + { return static_cast(m_ptr); } protected: - T m_ptr; + /*! @field m_ptr The pointer to the Objective-C object + @discussion + 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 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; }; -// The only thing wxObjcAutoRef has to do is retain an initial object +/*! + @class wxObjcAutoRef + @description + A pointer holder that does retain its argument. + NOTE: It is suggest that you instead use wxObjcAutoRefFromAlloc foo([aRawPointer retain]) + */ template class wxObjcAutoRef: public wxObjcAutoRefFromAlloc { public: + /*! @method wxObjcAutoRef + @description + Uses the underlying wxObjcAutoRefFromAlloc and simply does a typical [p retain] such that + in RR-mode the object is in effectively the same retain-count state as it would have been + coming straight from an alloc method. + */ wxObjcAutoRef(T p = 0) : wxObjcAutoRefFromAlloc(p) - { ObjcRetain(m_ptr); } + { // NOTE: ObjcRetain is correct because in GC-mode it balances ObjcRelease in our superclass constructor + // In RR mode it does retain and the superclass does retain/release thus resulting in an overall retain. + ObjcRetain(static_cast(wxObjcAutoRefFromAlloc::m_ptr)); + } ~wxObjcAutoRef() {} wxObjcAutoRef(const wxObjcAutoRef& otherRef) : wxObjcAutoRefFromAlloc(otherRef)