mirror of
https://github.com/gnustep/libs-base.git
synced 2025-06-01 09:02:01 +00:00
Preliminary code for emulation of the objc2 runtime weak reference feature
This commit is contained in:
parent
e7716e6ed1
commit
927e97eaac
11 changed files with 472 additions and 81 deletions
|
@ -132,6 +132,11 @@ extern "C" {
|
||||||
* GSI_MAP_ZEROED()
|
* GSI_MAP_ZEROED()
|
||||||
* Define this macro to check whether a map uses keys which may
|
* Define this macro to check whether a map uses keys which may
|
||||||
* be zeroed weak pointers.
|
* be zeroed weak pointers.
|
||||||
|
*
|
||||||
|
* GSI_MAP_NODE_CLASS
|
||||||
|
* If defined, each node in the map has an 'isa' field at the
|
||||||
|
* start which is initialised to be the specified class so that
|
||||||
|
* the node looks like an instance of that class.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef GSI_MAP_HAS_VALUE
|
#ifndef GSI_MAP_HAS_VALUE
|
||||||
|
@ -384,6 +389,9 @@ typedef GSIMapNode_t *GSIMapNode;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct _GSIMapNode {
|
struct _GSIMapNode {
|
||||||
|
#if defined(GSI_MAP_NODE_CLASS)
|
||||||
|
void *isa; /* Node pretends to be instance of class */
|
||||||
|
#endif
|
||||||
GSIMapNode nextInBucket; /* Linked list of bucket. */
|
GSIMapNode nextInBucket; /* Linked list of bucket. */
|
||||||
GSIMapKey key;
|
GSIMapKey key;
|
||||||
#if GSI_MAP_HAS_VALUE
|
#if GSI_MAP_HAS_VALUE
|
||||||
|
@ -1078,9 +1086,8 @@ GSIMapCountByEnumeratingWithStateObjectsCount(GSIMapTable map,
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if GSI_MAP_HAS_VALUE
|
|
||||||
GS_STATIC_INLINE GSIMapNode
|
GS_STATIC_INLINE GSIMapNode
|
||||||
GSIMapAddPairNoRetain(GSIMapTable map, GSIMapKey key, GSIMapVal value)
|
GSIMapGetNode(GSIMapTable map)
|
||||||
{
|
{
|
||||||
GSIMapNode node = map->freeNodes;
|
GSIMapNode node = map->freeNodes;
|
||||||
|
|
||||||
|
@ -1090,11 +1097,24 @@ GSIMapAddPairNoRetain(GSIMapTable map, GSIMapKey key, GSIMapVal value)
|
||||||
node = map->freeNodes;
|
node = map->freeNodes;
|
||||||
}
|
}
|
||||||
map->freeNodes = node->nextInBucket;
|
map->freeNodes = node->nextInBucket;
|
||||||
|
#if defined(GSI_MAP_NODE_CLASS)
|
||||||
|
node->isa = (void*)(GSI_MAP_NODE_CLASS);
|
||||||
|
#endif
|
||||||
|
node->nextInBucket = 0;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if GSI_MAP_HAS_VALUE
|
||||||
|
GS_STATIC_INLINE GSIMapNode
|
||||||
|
GSIMapAddPairNoRetain(GSIMapTable map, GSIMapKey key, GSIMapVal value)
|
||||||
|
{
|
||||||
|
GSIMapNode node = GSIMapGetNode(map);
|
||||||
|
|
||||||
node->key = key;
|
node->key = key;
|
||||||
node->value = value;
|
node->value = value;
|
||||||
GSI_MAP_STORE_KEY(map, &node->key, key);
|
GSI_MAP_STORE_KEY(map, &node->key, key);
|
||||||
GSI_MAP_STORE_VALUE(map, &node->value, value);
|
GSI_MAP_STORE_VALUE(map, &node->value, value);
|
||||||
node->nextInBucket = 0;
|
|
||||||
GSIMapRightSizeMap(map, map->nodeCount);
|
GSIMapRightSizeMap(map, map->nodeCount);
|
||||||
GSIMapAddNodeToMap(map, node);
|
GSIMapAddNodeToMap(map, node);
|
||||||
return node;
|
return node;
|
||||||
|
@ -1103,19 +1123,12 @@ GSIMapAddPairNoRetain(GSIMapTable map, GSIMapKey key, GSIMapVal value)
|
||||||
GS_STATIC_INLINE GSIMapNode
|
GS_STATIC_INLINE GSIMapNode
|
||||||
GSIMapAddPair(GSIMapTable map, GSIMapKey key, GSIMapVal value)
|
GSIMapAddPair(GSIMapTable map, GSIMapKey key, GSIMapVal value)
|
||||||
{
|
{
|
||||||
GSIMapNode node = map->freeNodes;
|
GSIMapNode node = GSIMapGetNode(map);
|
||||||
|
|
||||||
if (node == 0)
|
|
||||||
{
|
|
||||||
GSIMapMoreNodes(map, map->nodeCount < map->increment ? 0: map->increment);
|
|
||||||
node = map->freeNodes;
|
|
||||||
}
|
|
||||||
map->freeNodes = node->nextInBucket;
|
|
||||||
GSI_MAP_STORE_KEY(map, &node->key, key);
|
GSI_MAP_STORE_KEY(map, &node->key, key);
|
||||||
GSI_MAP_RETAIN_KEY(map, node->key);
|
GSI_MAP_RETAIN_KEY(map, node->key);
|
||||||
GSI_MAP_STORE_VALUE(map, &node->value, value);
|
GSI_MAP_STORE_VALUE(map, &node->value, value);
|
||||||
GSI_MAP_RETAIN_VALUE(map, node->value);
|
GSI_MAP_RETAIN_VALUE(map, node->value);
|
||||||
node->nextInBucket = 0;
|
|
||||||
GSIMapRightSizeMap(map, map->nodeCount);
|
GSIMapRightSizeMap(map, map->nodeCount);
|
||||||
GSIMapAddNodeToMap(map, node);
|
GSIMapAddNodeToMap(map, node);
|
||||||
return node;
|
return node;
|
||||||
|
@ -1124,16 +1137,9 @@ GSIMapAddPair(GSIMapTable map, GSIMapKey key, GSIMapVal value)
|
||||||
GS_STATIC_INLINE GSIMapNode
|
GS_STATIC_INLINE GSIMapNode
|
||||||
GSIMapAddKeyNoRetain(GSIMapTable map, GSIMapKey key)
|
GSIMapAddKeyNoRetain(GSIMapTable map, GSIMapKey key)
|
||||||
{
|
{
|
||||||
GSIMapNode node = map->freeNodes;
|
GSIMapNode node = GSIMapGetNode(map);
|
||||||
|
|
||||||
if (node == 0)
|
|
||||||
{
|
|
||||||
GSIMapMoreNodes(map, map->nodeCount < map->increment ? 0: map->increment);
|
|
||||||
node = map->freeNodes;
|
|
||||||
}
|
|
||||||
map->freeNodes = node->nextInBucket;
|
|
||||||
GSI_MAP_STORE_KEY(map, &node->key, key);
|
GSI_MAP_STORE_KEY(map, &node->key, key);
|
||||||
node->nextInBucket = 0;
|
|
||||||
GSIMapRightSizeMap(map, map->nodeCount);
|
GSIMapRightSizeMap(map, map->nodeCount);
|
||||||
GSIMapAddNodeToMap(map, node);
|
GSIMapAddNodeToMap(map, node);
|
||||||
return node;
|
return node;
|
||||||
|
@ -1142,17 +1148,10 @@ GSIMapAddKeyNoRetain(GSIMapTable map, GSIMapKey key)
|
||||||
GS_STATIC_INLINE GSIMapNode
|
GS_STATIC_INLINE GSIMapNode
|
||||||
GSIMapAddKey(GSIMapTable map, GSIMapKey key)
|
GSIMapAddKey(GSIMapTable map, GSIMapKey key)
|
||||||
{
|
{
|
||||||
GSIMapNode node = map->freeNodes;
|
GSIMapNode node = GSIMapGetNode(map);
|
||||||
|
|
||||||
if (node == 0)
|
|
||||||
{
|
|
||||||
GSIMapMoreNodes(map, map->nodeCount < map->increment ? 0: map->increment);
|
|
||||||
node = map->freeNodes;
|
|
||||||
}
|
|
||||||
map->freeNodes = node->nextInBucket;
|
|
||||||
GSI_MAP_STORE_KEY(map, &node->key, key);
|
GSI_MAP_STORE_KEY(map, &node->key, key);
|
||||||
GSI_MAP_RETAIN_KEY(map, node->key);
|
GSI_MAP_RETAIN_KEY(map, node->key);
|
||||||
node->nextInBucket = 0;
|
|
||||||
GSIMapRightSizeMap(map, map->nodeCount);
|
GSIMapRightSizeMap(map, map->nodeCount);
|
||||||
GSIMapAddNodeToMap(map, node);
|
GSIMapAddNodeToMap(map, node);
|
||||||
return node;
|
return node;
|
||||||
|
|
|
@ -637,5 +637,13 @@ void
|
||||||
GSPrivateEncodeBase64(const uint8_t *src, NSUInteger length, uint8_t *dst)
|
GSPrivateEncodeBase64(const uint8_t *src, NSUInteger length, uint8_t *dst)
|
||||||
GS_ATTRIB_PRIVATE;
|
GS_ATTRIB_PRIVATE;
|
||||||
|
|
||||||
|
#ifndef OBJC_CAP_ARC
|
||||||
|
/* When we don't have a runtime with ARC to support weak references, we
|
||||||
|
* use our own version.
|
||||||
|
*/
|
||||||
|
void GSWeakInit() GS_ATTRIB_PRIVATE;
|
||||||
|
BOOL objc_delete_weak_refs(id obj);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* _GSPrivate_h_ */
|
#endif /* _GSPrivate_h_ */
|
||||||
|
|
||||||
|
|
|
@ -105,8 +105,8 @@ typedef GSIMapNode_t *GSIMapNode;
|
||||||
pointerFunctionsReplace(&M->cb.pf, (void**)addr, (x).obj);
|
pointerFunctionsReplace(&M->cb.pf, (void**)addr, (x).obj);
|
||||||
|
|
||||||
#define GSI_MAP_READ_KEY(M,addr) \
|
#define GSI_MAP_READ_KEY(M,addr) \
|
||||||
(M->legacy ? *(addr) :\
|
(M->legacy ? *(addr) :\
|
||||||
(__typeof__(*addr))pointerFunctionsRead(&M->cb.pf, (void**)addr))
|
(__typeof__(*addr))pointerFunctionsRead(&M->cb.pf, (void**)addr))
|
||||||
|
|
||||||
#define GSI_MAP_ENUMERATOR NSHashEnumerator
|
#define GSI_MAP_ENUMERATOR NSHashEnumerator
|
||||||
|
|
||||||
|
|
|
@ -29,14 +29,16 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(OBJC_CAP_ARC)
|
#if defined(OBJC_CAP_ARC)
|
||||||
# include <objc/objc-arc.h>
|
# include <objc/objc-arc.h>
|
||||||
# define WEAK_READ(x) objc_loadWeak((id*)x)
|
# define WEAK_READ(x) objc_loadWeak((id*)x)
|
||||||
# define WEAK_WRITE(addr, x) objc_storeWeak((id*)addr, (id)x)
|
# define WEAK_WRITE(addr, x) objc_storeWeak((id*)addr, (id)x)
|
||||||
# define STRONG_WRITE(addr, x) objc_storeStrong((id*)addr, (id)x)
|
# define STRONG_WRITE(addr, x) objc_storeStrong((id*)addr, (id)x)
|
||||||
# define STRONG_ACQUIRE(x) objc_retain(x)
|
# define STRONG_ACQUIRE(x) objc_retain(x)
|
||||||
#else
|
#else
|
||||||
# define WEAK_READ(x) (*x)
|
extern id objc_loadWeak(id *object);
|
||||||
# define WEAK_WRITE(addr, x) (*(addr) = x)
|
extern id objc_storeWeak(id *addr, id obj);
|
||||||
|
# define WEAK_READ(x) objc_loadWeak((id*)x)
|
||||||
|
# define WEAK_WRITE(addr, x) objc_storeWeak((id*)addr, (id)x)
|
||||||
# define STRONG_WRITE(addr, x) ASSIGN(*((id*)addr), ((id)x))
|
# define STRONG_WRITE(addr, x) ASSIGN(*((id*)addr), ((id)x))
|
||||||
# define STRONG_ACQUIRE(x) RETAIN(((id)x))
|
# define STRONG_ACQUIRE(x) RETAIN(((id)x))
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -493,9 +493,7 @@ static BOOL objc_release_fast_no_destroy_internal(id anObject)
|
||||||
* have been greater than zero)
|
* have been greater than zero)
|
||||||
*/
|
*/
|
||||||
(((obj)anObject)[-1].retained) = 0;
|
(((obj)anObject)[-1].retained) = 0;
|
||||||
# ifdef OBJC_CAP_ARC
|
|
||||||
objc_delete_weak_refs(anObject);
|
objc_delete_weak_refs(anObject);
|
||||||
# endif
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
#else /* GSATOMICREAD */
|
#else /* GSATOMICREAD */
|
||||||
|
@ -504,9 +502,7 @@ static BOOL objc_release_fast_no_destroy_internal(id anObject)
|
||||||
pthread_mutex_lock(theLock);
|
pthread_mutex_lock(theLock);
|
||||||
if (((obj)anObject)[-1].retained == 0)
|
if (((obj)anObject)[-1].retained == 0)
|
||||||
{
|
{
|
||||||
# ifdef OBJC_CAP_ARC
|
|
||||||
objc_delete_weak_refs(anObject);
|
objc_delete_weak_refs(anObject);
|
||||||
# endif
|
|
||||||
pthread_mutex_unlock(theLock);
|
pthread_mutex_unlock(theLock);
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
@ -947,6 +943,8 @@ static id gs_weak_load(id obj)
|
||||||
{
|
{
|
||||||
#ifdef OBJC_CAP_ARC
|
#ifdef OBJC_CAP_ARC
|
||||||
_objc_weak_load = gs_weak_load;
|
_objc_weak_load = gs_weak_load;
|
||||||
|
#else
|
||||||
|
GSWeakInit();
|
||||||
#endif
|
#endif
|
||||||
objc_create_block_classes_as_subclasses_of(self);
|
objc_create_block_classes_as_subclasses_of(self);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,11 @@ ObjectiveC2_OBJC_FILES += \
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(CC), clang)
|
ifeq ($(CC), clang)
|
||||||
|
# We need the flag for blocks support and we have ARC built in.
|
||||||
ADDITIONAL_OBJCFLAGS = -fblocks
|
ADDITIONAL_OBJCFLAGS = -fblocks
|
||||||
|
else
|
||||||
|
# We need to emulated the weak reference API from the ARC runtime.
|
||||||
|
ObjectiveC2_OBJC_FILES += weak.m
|
||||||
endif
|
endif
|
||||||
|
|
||||||
-include Makefile.preamble
|
-include Makefile.preamble
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
ObjectiveC2 framework implements the new Apple runtime APIs, introduced with
|
ObjectiveC2 framework implements the new Apple runtime APIs, introduced with
|
||||||
OS X 10.5 on top of the GCC Objective-C runtime. Its use is now discouraged.
|
OS X 10.5 (and later) on top of the GCC Objective-C runtime. Much code has
|
||||||
This code has been merged into the GNUstep Objective-C-2.0 runtime, which can
|
been merged into the GNUstep Objective-C-2.0 runtime, which is used when
|
||||||
act as a drop-in replacement for GCC libobjc. You can find the code here:
|
compiling with clang (svn://svn.gna.org/svn/gnustep/libs/libobjc2/trunk).
|
||||||
|
Somewhat recent versions of the GNU runtime implement much of the newer Apple
|
||||||
svn://svn.gna.org/svn/gnustep/libs/libobjc2/trunk
|
APIs.
|
||||||
|
|
413
Source/ObjectiveC2/weak.m
Normal file
413
Source/ObjectiveC2/weak.m
Normal file
|
@ -0,0 +1,413 @@
|
||||||
|
|
||||||
|
/* Emulation of ARC runtime support for weak references based on the gnustep
|
||||||
|
* runtime implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "common.h"
|
||||||
|
#import "Foundation/Foundation.h"
|
||||||
|
#import "../GSPrivate.h"
|
||||||
|
#import "../GSPThread.h"
|
||||||
|
|
||||||
|
/* This function needs to identify objects which should NOT be handled by
|
||||||
|
* weak references.
|
||||||
|
* Nil is a special case which can not be stored as a weak reference because
|
||||||
|
* it indicates the absence of an object etc.
|
||||||
|
* Persistent objects do not need any sort of weak (or strong) reference and
|
||||||
|
* if they are immutable then trying to mark them as referenced would crash.
|
||||||
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
|
static inline BOOL
|
||||||
|
isPersistentObject(id obj)
|
||||||
|
{
|
||||||
|
if (obj == nil)
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
if (object_getClass(obj) == [NSConstantString class])
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int WeakRefClass = 0;
|
||||||
|
|
||||||
|
#define GSI_MAP_NODE_CLASS (&WeakRefClass)
|
||||||
|
#define GSI_MAP_CLEAR_KEY(M, X) ((*X).obj = nil)
|
||||||
|
#define GSI_MAP_HASH(M, X) ((NSUInteger)(X.obj) >> 2)
|
||||||
|
#define GSI_MAP_EQUAL(M, X, Y) (X.obj == Y.obj)
|
||||||
|
#define GSI_MAP_RETAIN_KEY(M, X)
|
||||||
|
#define GSI_MAP_RELEASE_KEY(M, X)
|
||||||
|
#define GSI_MAP_RETAIN_VAL(M, X)
|
||||||
|
#define GSI_MAP_RELEASE_VAL(M, X)
|
||||||
|
#define GSI_MAP_KTYPES GSUNION_OBJ
|
||||||
|
#define GSI_MAP_VTYPES GSUNION_NSINT
|
||||||
|
|
||||||
|
#include "GNUstepBase/GSIMap.h"
|
||||||
|
|
||||||
|
typedef GSIMapNode_t WeakRef;
|
||||||
|
|
||||||
|
static gs_mutex_t weakLock = GS_MUTEX_INIT_STATIC;
|
||||||
|
|
||||||
|
/* The weakRefs table contains weak references (nodes) for weak references
|
||||||
|
* to any active objects.
|
||||||
|
*/
|
||||||
|
static GSIMapTable_t weakRefs = { 0 };
|
||||||
|
|
||||||
|
/* The deallocated list contains the weak references (nodes) for objects
|
||||||
|
* which have already been deallocated (so the references are now to nil).
|
||||||
|
*/
|
||||||
|
static WeakRef *deallocated = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
/* This must be called on startup before any weak references are taken.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
GSWeakInit()
|
||||||
|
{
|
||||||
|
GS_MUTEX_LOCK(weakLock);
|
||||||
|
if (0 == weakRefs.increment)
|
||||||
|
{
|
||||||
|
GSIMapInitWithZoneAndCapacity(
|
||||||
|
&weakRefs, NSDefaultMallocZone(), 1024);
|
||||||
|
}
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load from a weak pointer and return whether this really was a weak
|
||||||
|
* reference or a strong (not deallocatable) object in a weak pointer.
|
||||||
|
* The object will be stored in 'obj' and the weak reference in 'ref',
|
||||||
|
* if one exists.
|
||||||
|
*/
|
||||||
|
inline static BOOL
|
||||||
|
loadWeakPointer(id *addr, id *obj, WeakRef **ref)
|
||||||
|
{
|
||||||
|
id oldObj = *addr;
|
||||||
|
|
||||||
|
if (nil == oldObj)
|
||||||
|
{
|
||||||
|
*ref = NULL;
|
||||||
|
*obj = nil;
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
if (*(void**)oldObj == (void*)&WeakRefClass)
|
||||||
|
{
|
||||||
|
*ref = (WeakRef*)oldObj;
|
||||||
|
*obj = (*ref)->key.obj;
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
*ref = NULL;
|
||||||
|
*obj = oldObj;
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((always_inline))
|
||||||
|
static inline BOOL
|
||||||
|
weakRefRelease(WeakRef *ref)
|
||||||
|
{
|
||||||
|
ref->value.nsi--;
|
||||||
|
if (ref->value.nsi == 0)
|
||||||
|
{
|
||||||
|
if (nil == ref->key.obj)
|
||||||
|
{
|
||||||
|
/* The object was already deallocated so we must remove this
|
||||||
|
* reference from the deallocated list.
|
||||||
|
*/
|
||||||
|
if (deallocated == ref)
|
||||||
|
{
|
||||||
|
deallocated = ref->nextInBucket;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WeakRef *tmp = deallocated;
|
||||||
|
|
||||||
|
while (tmp->nextInBucket != 0)
|
||||||
|
{
|
||||||
|
if (tmp->nextInBucket == ref)
|
||||||
|
{
|
||||||
|
tmp->nextInBucket = ref->nextInBucket;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tmp = tmp->nextInBucket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ref->nextInBucket = weakRefs.freeNodes;
|
||||||
|
weakRefs.freeNodes = ref;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GSIMapBucket bucket = GSIMapBucketForKey(&weakRefs, ref->key);
|
||||||
|
|
||||||
|
GSIMapRemoveNodeFromMap(&weakRefs, bucket, ref);
|
||||||
|
GSIMapFreeNode(&weakRefs, ref);
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We should record the fact that the object has weak references (unless
|
||||||
|
* it is a persistent one).
|
||||||
|
* Return YES if the object is persistent and should not have weak references,
|
||||||
|
* NO otherwise.
|
||||||
|
*/
|
||||||
|
static BOOL
|
||||||
|
setObjectHasWeakRefs(id obj)
|
||||||
|
{
|
||||||
|
BOOL isPersistent = isPersistentObject(obj);
|
||||||
|
|
||||||
|
if (NO == isPersistent)
|
||||||
|
{
|
||||||
|
/* FIXME ... for performance we should mark the object as having
|
||||||
|
* weak references and we should check that in objc_delete_weak_refs()
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
return isPersistent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WeakRef *
|
||||||
|
incrementWeakRefCount(id obj)
|
||||||
|
{
|
||||||
|
GSIMapKey key;
|
||||||
|
GSIMapBucket bucket;
|
||||||
|
WeakRef *ref;
|
||||||
|
|
||||||
|
key.obj = obj;
|
||||||
|
bucket = GSIMapBucketForKey(&weakRefs, key);
|
||||||
|
ref = GSIMapNodeForKeyInBucket(&weakRefs, bucket, key);
|
||||||
|
if (NULL == ref)
|
||||||
|
{
|
||||||
|
ref = GSIMapGetNode(&weakRefs);
|
||||||
|
|
||||||
|
ref->key.obj = obj;
|
||||||
|
ref->value.nsi = 1;
|
||||||
|
GSIMapAddNodeToBucket(bucket, ref);
|
||||||
|
weakRefs.nodeCount++;
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
ref->value.nsi++;
|
||||||
|
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
id
|
||||||
|
objc_storeWeak(id *addr, id obj)
|
||||||
|
{
|
||||||
|
WeakRef *oldRef;
|
||||||
|
id old;
|
||||||
|
BOOL isGlobalObject;
|
||||||
|
|
||||||
|
GS_MUTEX_LOCK(weakLock);
|
||||||
|
loadWeakPointer(addr, &old, &oldRef);
|
||||||
|
/* If the old and new values are the same (and we are not setting a nil
|
||||||
|
* value to destroy an existing weak reference), then we don't need to
|
||||||
|
* do anything.
|
||||||
|
*/
|
||||||
|
if ((obj != nil || oldRef == NULL) && old == obj)
|
||||||
|
{
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
isGlobalObject = setObjectHasWeakRefs(obj);
|
||||||
|
|
||||||
|
/* If we old ref exists, decrement its reference count. This may also
|
||||||
|
* delete the weak reference from the map.
|
||||||
|
*/
|
||||||
|
if (oldRef != NULL)
|
||||||
|
{
|
||||||
|
weakRefRelease(oldRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we're storing nil, then just write a null pointer.
|
||||||
|
*/
|
||||||
|
if (nil == obj)
|
||||||
|
{
|
||||||
|
*addr = obj;
|
||||||
|
}
|
||||||
|
else if (isGlobalObject)
|
||||||
|
{
|
||||||
|
/* If this is a global object, it's never deallocated,
|
||||||
|
* so we don't make this a weak reference.
|
||||||
|
*/
|
||||||
|
*addr = obj;
|
||||||
|
}
|
||||||
|
else if ([obj retainCount] == 0)
|
||||||
|
{
|
||||||
|
/* The object is being deallocated ... we must store nil.
|
||||||
|
*/
|
||||||
|
*addr = obj = nil;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*addr = (id)incrementWeakRefCount(obj);
|
||||||
|
}
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function called when objects are deallocated
|
||||||
|
*/
|
||||||
|
BOOL
|
||||||
|
objc_delete_weak_refs(id obj)
|
||||||
|
{
|
||||||
|
GSIMapKey key;
|
||||||
|
GSIMapBucket bucket;
|
||||||
|
WeakRef *ref;
|
||||||
|
|
||||||
|
/* FIXME ... for performance we should have marked the object as having
|
||||||
|
* weak references and we should check that in order to avoid the cost
|
||||||
|
* of the map table lookup when it's not needed.
|
||||||
|
*/
|
||||||
|
if (0)
|
||||||
|
{
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
key.obj = obj;
|
||||||
|
GS_MUTEX_LOCK(weakLock);
|
||||||
|
bucket = GSIMapBucketForKey(&weakRefs, key);
|
||||||
|
ref = GSIMapNodeForKeyInBucket(&weakRefs, bucket, key);
|
||||||
|
if (ref)
|
||||||
|
{
|
||||||
|
GSIMapRemoveNodeFromBucket(bucket, ref);
|
||||||
|
ref->key.obj = nil;
|
||||||
|
weakRefs.nodeCount--;
|
||||||
|
/* The object is deallocated but there are still weak references
|
||||||
|
* to it so we put the weak reference node in the deallocated list.
|
||||||
|
*/
|
||||||
|
ref->nextInBucket = deallocated;
|
||||||
|
deallocated = ref;
|
||||||
|
}
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
id
|
||||||
|
objc_loadWeakRetained(id *addr)
|
||||||
|
{
|
||||||
|
id obj;
|
||||||
|
WeakRef *ref;
|
||||||
|
|
||||||
|
GS_MUTEX_LOCK(weakLock);
|
||||||
|
|
||||||
|
/* If this is not actually a weak pointer, return the object directly.
|
||||||
|
*/
|
||||||
|
if (!loadWeakPointer(addr, &obj, &ref))
|
||||||
|
{
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nil == obj)
|
||||||
|
{
|
||||||
|
/* The object has been destroed so we should remove the weak
|
||||||
|
* reference to it.
|
||||||
|
*/
|
||||||
|
if (ref != NULL)
|
||||||
|
{
|
||||||
|
weakRefRelease(ref);
|
||||||
|
*addr = nil;
|
||||||
|
}
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = [obj retain];
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
id
|
||||||
|
objc_loadWeak(id *object)
|
||||||
|
{
|
||||||
|
return [objc_loadWeakRetained(object) autorelease];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
objc_copyWeak(id *dest, id *src)
|
||||||
|
{
|
||||||
|
/* Don't retain or release.
|
||||||
|
* 'src' is a valid pointer to a __weak pointer or nil.
|
||||||
|
* 'dest' is a valid pointer to uninitialised memory.
|
||||||
|
* After this operation, 'dest' should contain whatever 'src' contained.
|
||||||
|
*/
|
||||||
|
id obj;
|
||||||
|
WeakRef *srcRef;
|
||||||
|
|
||||||
|
GS_MUTEX_LOCK(weakLock);
|
||||||
|
loadWeakPointer(src, &obj, &srcRef);
|
||||||
|
*dest = *src;
|
||||||
|
if (srcRef)
|
||||||
|
{
|
||||||
|
srcRef->value.nsi++;
|
||||||
|
}
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
objc_moveWeak(id *dest, id *src)
|
||||||
|
{
|
||||||
|
/* Don't retain or release.
|
||||||
|
* 'dest' is a valid pointer to uninitialized memory.
|
||||||
|
* 'src' is a valid pointer to a __weak pointer.
|
||||||
|
* This operation moves from *src to *dest and must be atomic with respect
|
||||||
|
* to other stores to *src via 'objc_storeWeak'.
|
||||||
|
*/
|
||||||
|
GS_MUTEX_LOCK(weakLock);
|
||||||
|
*dest = *src;
|
||||||
|
*src = nil;
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
objc_destroyWeak(id *obj)
|
||||||
|
{
|
||||||
|
WeakRef *oldRef;
|
||||||
|
id old;
|
||||||
|
|
||||||
|
GS_MUTEX_LOCK(weakLock);
|
||||||
|
loadWeakPointer(obj, &old, &oldRef);
|
||||||
|
/* If the old ref exists, decrement its reference count.
|
||||||
|
* This may also remove the weak reference from the map.
|
||||||
|
*/
|
||||||
|
if (oldRef != NULL)
|
||||||
|
{
|
||||||
|
weakRefRelease(oldRef);
|
||||||
|
}
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
id
|
||||||
|
objc_initWeak(id *addr, id obj)
|
||||||
|
{
|
||||||
|
BOOL isGlobalObject;
|
||||||
|
|
||||||
|
if (nil == obj)
|
||||||
|
{
|
||||||
|
*addr = nil;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
GS_MUTEX_LOCK(weakLock);
|
||||||
|
isGlobalObject = setObjectHasWeakRefs(obj);
|
||||||
|
if (isGlobalObject)
|
||||||
|
{
|
||||||
|
*addr = obj;
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([obj retainCount] == 0)
|
||||||
|
{
|
||||||
|
*addr = nil;
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
if (nil != obj)
|
||||||
|
{
|
||||||
|
*(WeakRef**)addr = incrementWeakRefCount(obj);
|
||||||
|
}
|
||||||
|
GS_MUTEX_UNLOCK(weakLock);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
|
@ -1,25 +1,14 @@
|
||||||
#import "ObjectTesting.h"
|
#import "ObjectTesting.h"
|
||||||
#import <Foundation/NSAutoreleasePool.h>
|
#import <Foundation/NSAutoreleasePool.h>
|
||||||
#import <Foundation/NSHashTable.h>
|
#import <Foundation/NSHashTable.h>
|
||||||
#if __has_include(<objc/capabilities.h>)
|
|
||||||
#include <objc/capabilities.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
START_SET("NSHashTable weak objects")
|
START_SET("NSHashTable weak objects")
|
||||||
#if !__APPLE__ // We assume that apple systems support zeroing weak pointers
|
NSAutoreleasePool *arp = [NSAutoreleasePool new];
|
||||||
#ifdef OBJC_CAP_ARC
|
NSHashTable *hashTable = [NSHashTable weakObjectsHashTable];
|
||||||
if (!objc_test_capability(OBJC_CAP_ARC))
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
SKIP("ARC support unavailable")
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
NSAutoreleasePool *arp = [NSAutoreleasePool new];
|
|
||||||
NSHashTable *hashTable = [NSHashTable weakObjectsHashTable];
|
|
||||||
|
|
||||||
NSAutoreleasePool *arp2 = [NSAutoreleasePool new];
|
NSAutoreleasePool *arp2 = [NSAutoreleasePool new];
|
||||||
|
|
||||||
id testObj = [[[NSObject alloc] init] autorelease];
|
id testObj = [[[NSObject alloc] init] autorelease];
|
||||||
[hashTable addObject:testObj];
|
[hashTable addObject:testObj];
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
#import "ObjectTesting.h"
|
#import "ObjectTesting.h"
|
||||||
#import <Foundation/NSAutoreleasePool.h>
|
#import <Foundation/NSAutoreleasePool.h>
|
||||||
#import <Foundation/NSMapTable.h>
|
#import <Foundation/NSMapTable.h>
|
||||||
#if __has_include(<objc/capabilities.h>)
|
|
||||||
#include <objc/capabilities.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
START_SET("NSMapTable weak objects")
|
START_SET("NSMapTable weak objects")
|
||||||
#if !__APPLE__ // We assume that apple systems support zeroing weak pointers
|
|
||||||
#ifdef OBJC_CAP_ARC
|
|
||||||
if (!objc_test_capability(OBJC_CAP_ARC))
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
SKIP("ARC support unavailable")
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
NSAutoreleasePool *arp = [NSAutoreleasePool new];
|
NSAutoreleasePool *arp = [NSAutoreleasePool new];
|
||||||
NSMapTable *mapTable = [NSMapTable strongToWeakObjectsMapTable];
|
NSMapTable *mapTable = [NSMapTable strongToWeakObjectsMapTable];
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
#import "ObjectTesting.h"
|
#import "ObjectTesting.h"
|
||||||
#import <Foundation/NSAutoreleasePool.h>
|
#import <Foundation/NSAutoreleasePool.h>
|
||||||
#import <Foundation/NSPointerArray.h>
|
#import <Foundation/NSPointerArray.h>
|
||||||
#if __has_include(<objc/capabilities.h>)
|
|
||||||
#include <objc/capabilities.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
START_SET("NSPointerArray weak objects")
|
START_SET("NSPointerArray weak objects")
|
||||||
#if !__APPLE__ // We assume that apple systems support zeroing weak pointers
|
|
||||||
#ifdef OBJC_CAP_ARC
|
|
||||||
if (!objc_test_capability(OBJC_CAP_ARC))
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
SKIP("ARC support unavailable")
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
NSAutoreleasePool *arp = [NSAutoreleasePool new];
|
NSAutoreleasePool *arp = [NSAutoreleasePool new];
|
||||||
NSPointerArray *array = [NSPointerArray weakObjectsPointerArray];
|
NSPointerArray *array = [NSPointerArray weakObjectsPointerArray];
|
||||||
int index;
|
int index;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue