From ddfdef64fadb32a8c122977ff70669a9d8d5a37d Mon Sep 17 00:00:00 2001 From: David Elliott Date: Wed, 6 Feb 2008 20:10:07 +0000 Subject: [PATCH] Fix most of the Objective-C GC problems by using the stronger CFRetain/CFRelease in wxObjcAutoRef. Actually use wxObjcAutoRef for the wxNSViewNotificationObserver singleton to keep it from being finalized. Copyright 2008 Software 2000 Ltd. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@51576 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/cocoa/NSView.h | 4 +- include/wx/cocoa/ObjcRef.h | 81 +++++++++++++++++++++++---- include/wx/mac/corefoundation/cfref.h | 5 ++ src/cocoa/NSView.mm | 7 ++- 4 files changed, 83 insertions(+), 14 deletions(-) diff --git a/include/wx/cocoa/NSView.h b/include/wx/cocoa/NSView.h index 9cea1d678a..4081d71eda 100644 --- a/include/wx/cocoa/NSView.h +++ b/include/wx/cocoa/NSView.h @@ -21,6 +21,8 @@ typedef struct CGRect NSRect; typedef struct _NSRect NSRect; #endif +struct objc_object; + class wxWindow; WX_DECLARE_OBJC_HASHMAP(NSView); @@ -32,7 +34,7 @@ public: void AssociateNSView(WX_NSView cocoaNSView); void DisassociateNSView(WX_NSView cocoaNSView); protected: - static void *sm_cocoaObserver; + static struct objc_object *sm_cocoaObserver; public: virtual wxWindow* GetWxWindow() const { return NULL; } diff --git a/include/wx/cocoa/ObjcRef.h b/include/wx/cocoa/ObjcRef.h index 17493d14da..33086d2ab8 100644 --- a/include/wx/cocoa/ObjcRef.h +++ b/include/wx/cocoa/ObjcRef.h @@ -11,6 +11,10 @@ #ifndef _WX_COCOA_OBJCREF_H__ #define _WX_COCOA_OBJCREF_H__ + +// Reuse wxCFRef-related code (e.g. wxCFRetain/wxCFRelease) +#include "wx/mac/corefoundation/cfref.h" + /* wxObjcAutoRefFromAlloc: construct a reference to an object that was [NSObject -alloc]'ed and thus does not need a retain @@ -24,12 +28,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 +61,69 @@ 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] + wxCFRetain(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); } + { wxCFRetain(m_ptr); } ~wxObjcAutoRefFromAlloc() - { ObjcRelease(m_ptr); } + { wxCFRelease(m_ptr); } wxObjcAutoRefFromAlloc& operator=(const wxObjcAutoRefFromAlloc& otherRef) - { ObjcRetain(otherRef.m_ptr); - ObjcRelease(m_ptr); + { wxCFRetain(otherRef.m_ptr); + wxCFRelease(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 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. + */ + 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(wxObjcAutoRefFromAlloc::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) diff --git a/include/wx/mac/corefoundation/cfref.h b/include/wx/mac/corefoundation/cfref.h index 694a19d7c9..f5251ab1c0 100644 --- a/include/wx/mac/corefoundation/cfref.h +++ b/include/wx/mac/corefoundation/cfref.h @@ -17,6 +17,11 @@ #ifndef _WX_MAC_COREFOUNDATION_CFREF_H__ #define _WX_MAC_COREFOUNDATION_CFREF_H__ +// Include unistd to ensure that NULL is defined +#include +// Include AvailabilityMacros for DEPRECATED_ATTRIBUTE +#include + // #include /* Don't include CFBase.h such that this header can be included from public * headers with minimal namespace pollution. diff --git a/src/cocoa/NSView.mm b/src/cocoa/NSView.mm index 5c4b2a5680..89d4c4c0c8 100644 --- a/src/cocoa/NSView.mm +++ b/src/cocoa/NSView.mm @@ -28,6 +28,7 @@ #import #import #include "wx/cocoa/objc/NSView.h" +#include "wx/cocoa/ObjcRef.h" // ---------------------------------------------------------------------------- // globals @@ -215,5 +216,7 @@ WX_DECLARE_GET_OBJC_CLASS(wxNSViewNotificationObserver,NSObject) @end // implementation wxNSViewNotificationObserver WX_IMPLEMENT_GET_OBJC_CLASS(wxNSViewNotificationObserver,NSObject) -void *wxCocoaNSView::sm_cocoaObserver = [[WX_GET_OBJC_CLASS(wxNSViewNotificationObserver) alloc] init]; - +// New CF-retained observer (this should have been using wxObjcAutoRefFromAlloc to begin with) +wxObjcAutoRefFromAlloc s_cocoaNSViewObserver([[WX_GET_OBJC_CLASS(wxNSViewNotificationObserver) alloc] init]); +// For compatibility with old code +id wxCocoaNSView::sm_cocoaObserver = s_cocoaNSViewObserver; -- 2.47.2