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
This commit is contained in:
David Elliott
2008-02-06 20:10:07 +00:00
parent 627c8d8993
commit ddfdef64fa
4 changed files with 83 additions and 14 deletions

View File

@@ -21,6 +21,8 @@ typedef struct CGRect NSRect;
typedef struct _NSRect NSRect; typedef struct _NSRect NSRect;
#endif #endif
struct objc_object;
class wxWindow; class wxWindow;
WX_DECLARE_OBJC_HASHMAP(NSView); WX_DECLARE_OBJC_HASHMAP(NSView);
@@ -32,7 +34,7 @@ public:
void AssociateNSView(WX_NSView cocoaNSView); void AssociateNSView(WX_NSView cocoaNSView);
void DisassociateNSView(WX_NSView cocoaNSView); void DisassociateNSView(WX_NSView cocoaNSView);
protected: protected:
static void *sm_cocoaObserver; static struct objc_object *sm_cocoaObserver;
public: public:
virtual wxWindow* GetWxWindow() const virtual wxWindow* GetWxWindow() const
{ return NULL; } { return NULL; }

View File

@@ -11,6 +11,10 @@
#ifndef _WX_COCOA_OBJCREF_H__ #ifndef _WX_COCOA_OBJCREF_H__
#define _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 wxObjcAutoRefFromAlloc: construct a reference to an object that was
[NSObject -alloc]'ed and thus does not need a retain [NSObject -alloc]'ed and thus does not need a retain
@@ -24,11 +28,31 @@ struct objc_object;
class wxObjcAutoRefBase class wxObjcAutoRefBase
{ {
protected: protected:
/*! @function ObjcRetain
@abstract Simply does [p retain].
*/
static struct objc_object* ObjcRetain(struct objc_object*); static struct objc_object* ObjcRetain(struct objc_object*);
/*! @function ObjcRelease
@abstract Simply does [p release].
*/
static void ObjcRelease(struct objc_object*); 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 T> template <class T>
class wxObjcAutoRefFromAlloc: wxObjcAutoRefBase class wxObjcAutoRefFromAlloc: wxObjcAutoRefBase
@@ -37,34 +61,69 @@ public:
wxObjcAutoRefFromAlloc(T p = 0) wxObjcAutoRefFromAlloc(T p = 0)
: m_ptr(p) : m_ptr(p)
// NOTE: this is from alloc. Do NOT retain // 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<T>(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) wxObjcAutoRefFromAlloc(const wxObjcAutoRefFromAlloc& otherRef)
: m_ptr(otherRef.m_ptr) : m_ptr(otherRef.m_ptr)
{ ObjcRetain(m_ptr); } { wxCFRetain(m_ptr); }
~wxObjcAutoRefFromAlloc() ~wxObjcAutoRefFromAlloc()
{ ObjcRelease(m_ptr); } { wxCFRelease(m_ptr); }
wxObjcAutoRefFromAlloc& operator=(const wxObjcAutoRefFromAlloc& otherRef) wxObjcAutoRefFromAlloc& operator=(const wxObjcAutoRefFromAlloc& otherRef)
{ ObjcRetain(otherRef.m_ptr); { wxCFRetain(otherRef.m_ptr);
ObjcRelease(m_ptr); wxCFRelease(m_ptr);
m_ptr = otherRef.m_ptr; m_ptr = otherRef.m_ptr;
return *this; return *this;
} }
operator T() const operator T() const
{ return m_ptr; } { return static_cast<T>(m_ptr); }
T operator->() const T operator->() const
{ return m_ptr; } { return static_cast<T>(m_ptr); }
protected: 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<T> foo([aRawPointer retain])
*/
template <class T> template <class T>
class wxObjcAutoRef: public wxObjcAutoRefFromAlloc<T> class wxObjcAutoRef: public wxObjcAutoRefFromAlloc<T>
{ {
public: 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) wxObjcAutoRef(T p = 0)
: wxObjcAutoRefFromAlloc<T>(p) : wxObjcAutoRefFromAlloc<T>(p)
{ ObjcRetain(wxObjcAutoRefFromAlloc<T>::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<T>(wxObjcAutoRefFromAlloc<T>::m_ptr));
}
~wxObjcAutoRef() {} ~wxObjcAutoRef() {}
wxObjcAutoRef(const wxObjcAutoRef& otherRef) wxObjcAutoRef(const wxObjcAutoRef& otherRef)
: wxObjcAutoRefFromAlloc<T>(otherRef) : wxObjcAutoRefFromAlloc<T>(otherRef)

View File

@@ -17,6 +17,11 @@
#ifndef _WX_MAC_COREFOUNDATION_CFREF_H__ #ifndef _WX_MAC_COREFOUNDATION_CFREF_H__
#define _WX_MAC_COREFOUNDATION_CFREF_H__ #define _WX_MAC_COREFOUNDATION_CFREF_H__
// Include unistd to ensure that NULL is defined
#include <unistd.h>
// Include AvailabilityMacros for DEPRECATED_ATTRIBUTE
#include <AvailabilityMacros.h>
// #include <CoreFoundation/CFBase.h> // #include <CoreFoundation/CFBase.h>
/* Don't include CFBase.h such that this header can be included from public /* Don't include CFBase.h such that this header can be included from public
* headers with minimal namespace pollution. * headers with minimal namespace pollution.

View File

@@ -28,6 +28,7 @@
#import <Foundation/NSNotification.h> #import <Foundation/NSNotification.h>
#import <Foundation/NSString.h> #import <Foundation/NSString.h>
#include "wx/cocoa/objc/NSView.h" #include "wx/cocoa/objc/NSView.h"
#include "wx/cocoa/ObjcRef.h"
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// globals // globals
@@ -215,5 +216,7 @@ WX_DECLARE_GET_OBJC_CLASS(wxNSViewNotificationObserver,NSObject)
@end // implementation wxNSViewNotificationObserver @end // implementation wxNSViewNotificationObserver
WX_IMPLEMENT_GET_OBJC_CLASS(wxNSViewNotificationObserver,NSObject) 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<wxNSViewNotificationObserver*> s_cocoaNSViewObserver([[WX_GET_OBJC_CLASS(wxNSViewNotificationObserver) alloc] init]);
// For compatibility with old code
id wxCocoaNSView::sm_cocoaObserver = s_cocoaNSViewObserver;