From 927e97eaac036b2f9af6d383910fc22e05dfbfd7 Mon Sep 17 00:00:00 2001 From: rfm Date: Mon, 23 Dec 2024 18:32:23 +0000 Subject: [PATCH] Preliminary code for emulation of the objc2 runtime weak reference feature --- Headers/GNUstepBase/GSIMap.h | 53 ++- Source/GSPrivate.h | 8 + Source/NSConcreteHashTable.m | 4 +- Source/NSConcretePointerFunctions.h | 16 +- Source/NSObject.m | 6 +- Source/ObjectiveC2/GNUmakefile | 4 + Source/ObjectiveC2/README | 10 +- Source/ObjectiveC2/weak.m | 413 ++++++++++++++++++++++++ Tests/base/NSHashTable/weakObjects.m | 17 +- Tests/base/NSMapTable/weakObjects.m | 11 - Tests/base/NSPointerArray/weakObjects.m | 11 - 11 files changed, 472 insertions(+), 81 deletions(-) create mode 100644 Source/ObjectiveC2/weak.m diff --git a/Headers/GNUstepBase/GSIMap.h b/Headers/GNUstepBase/GSIMap.h index f4fe9a367a..a0c4c073b1 100644 --- a/Headers/GNUstepBase/GSIMap.h +++ b/Headers/GNUstepBase/GSIMap.h @@ -132,6 +132,11 @@ extern "C" { * GSI_MAP_ZEROED() * Define this macro to check whether a map uses keys which may * 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 @@ -384,6 +389,9 @@ typedef GSIMapNode_t *GSIMapNode; #endif struct _GSIMapNode { +#if defined(GSI_MAP_NODE_CLASS) + void *isa; /* Node pretends to be instance of class */ +#endif GSIMapNode nextInBucket; /* Linked list of bucket. */ GSIMapKey key; #if GSI_MAP_HAS_VALUE @@ -1078,9 +1086,8 @@ GSIMapCountByEnumeratingWithStateObjectsCount(GSIMapTable map, return count; } -#if GSI_MAP_HAS_VALUE GS_STATIC_INLINE GSIMapNode -GSIMapAddPairNoRetain(GSIMapTable map, GSIMapKey key, GSIMapVal value) +GSIMapGetNode(GSIMapTable map) { GSIMapNode node = map->freeNodes; @@ -1090,11 +1097,24 @@ GSIMapAddPairNoRetain(GSIMapTable map, GSIMapKey key, GSIMapVal value) node = map->freeNodes; } 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->value = value; GSI_MAP_STORE_KEY(map, &node->key, key); GSI_MAP_STORE_VALUE(map, &node->value, value); - node->nextInBucket = 0; GSIMapRightSizeMap(map, map->nodeCount); GSIMapAddNodeToMap(map, node); return node; @@ -1103,19 +1123,12 @@ GSIMapAddPairNoRetain(GSIMapTable map, GSIMapKey key, GSIMapVal value) GS_STATIC_INLINE GSIMapNode 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_RETAIN_KEY(map, node->key); GSI_MAP_STORE_VALUE(map, &node->value, value); GSI_MAP_RETAIN_VALUE(map, node->value); - node->nextInBucket = 0; GSIMapRightSizeMap(map, map->nodeCount); GSIMapAddNodeToMap(map, node); return node; @@ -1124,16 +1137,9 @@ GSIMapAddPair(GSIMapTable map, GSIMapKey key, GSIMapVal value) GS_STATIC_INLINE GSIMapNode 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); - node->nextInBucket = 0; GSIMapRightSizeMap(map, map->nodeCount); GSIMapAddNodeToMap(map, node); return node; @@ -1142,17 +1148,10 @@ GSIMapAddKeyNoRetain(GSIMapTable map, GSIMapKey key) GS_STATIC_INLINE GSIMapNode 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_RETAIN_KEY(map, node->key); - node->nextInBucket = 0; GSIMapRightSizeMap(map, map->nodeCount); GSIMapAddNodeToMap(map, node); return node; diff --git a/Source/GSPrivate.h b/Source/GSPrivate.h index 5adc3a3b92..fdfe2d8a97 100644 --- a/Source/GSPrivate.h +++ b/Source/GSPrivate.h @@ -637,5 +637,13 @@ void GSPrivateEncodeBase64(const uint8_t *src, NSUInteger length, uint8_t *dst) 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_ */ diff --git a/Source/NSConcreteHashTable.m b/Source/NSConcreteHashTable.m index 3a4451aca3..b7e0b75e90 100644 --- a/Source/NSConcreteHashTable.m +++ b/Source/NSConcreteHashTable.m @@ -105,8 +105,8 @@ @interface NSConcreteHashTable : NSHashTable pointerFunctionsReplace(&M->cb.pf, (void**)addr, (x).obj); #define GSI_MAP_READ_KEY(M,addr) \ - (M->legacy ? *(addr) :\ - (__typeof__(*addr))pointerFunctionsRead(&M->cb.pf, (void**)addr)) + (M->legacy ? *(addr) :\ + (__typeof__(*addr))pointerFunctionsRead(&M->cb.pf, (void**)addr)) #define GSI_MAP_ENUMERATOR NSHashEnumerator diff --git a/Source/NSConcretePointerFunctions.h b/Source/NSConcretePointerFunctions.h index c66de8f5e2..55a8e99fea 100644 --- a/Source/NSConcretePointerFunctions.h +++ b/Source/NSConcretePointerFunctions.h @@ -29,14 +29,16 @@ #endif #if defined(OBJC_CAP_ARC) -# include -# define WEAK_READ(x) objc_loadWeak((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_ACQUIRE(x) objc_retain(x) +# include +# define WEAK_READ(x) objc_loadWeak((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_ACQUIRE(x) objc_retain(x) #else -# define WEAK_READ(x) (*x) -# define WEAK_WRITE(addr, x) (*(addr) = x) +extern id objc_loadWeak(id *object); +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_ACQUIRE(x) RETAIN(((id)x)) #endif diff --git a/Source/NSObject.m b/Source/NSObject.m index cfbb579960..cb6b2d9aa2 100644 --- a/Source/NSObject.m +++ b/Source/NSObject.m @@ -493,9 +493,7 @@ static BOOL objc_release_fast_no_destroy_internal(id anObject) * have been greater than zero) */ (((obj)anObject)[-1].retained) = 0; -# ifdef OBJC_CAP_ARC objc_delete_weak_refs(anObject); -# endif return YES; } #else /* GSATOMICREAD */ @@ -504,9 +502,7 @@ static BOOL objc_release_fast_no_destroy_internal(id anObject) pthread_mutex_lock(theLock); if (((obj)anObject)[-1].retained == 0) { -# ifdef OBJC_CAP_ARC objc_delete_weak_refs(anObject); -# endif pthread_mutex_unlock(theLock); return YES; } @@ -947,6 +943,8 @@ + (void) load { #ifdef OBJC_CAP_ARC _objc_weak_load = gs_weak_load; +#else + GSWeakInit(); #endif objc_create_block_classes_as_subclasses_of(self); } diff --git a/Source/ObjectiveC2/GNUmakefile b/Source/ObjectiveC2/GNUmakefile index 1374403422..e375d0077a 100644 --- a/Source/ObjectiveC2/GNUmakefile +++ b/Source/ObjectiveC2/GNUmakefile @@ -56,7 +56,11 @@ ObjectiveC2_OBJC_FILES += \ endif ifeq ($(CC), clang) + # We need the flag for blocks support and we have ARC built in. ADDITIONAL_OBJCFLAGS = -fblocks +else + # We need to emulated the weak reference API from the ARC runtime. + ObjectiveC2_OBJC_FILES += weak.m endif -include Makefile.preamble diff --git a/Source/ObjectiveC2/README b/Source/ObjectiveC2/README index 9c51d730e0..01beab29bb 100644 --- a/Source/ObjectiveC2/README +++ b/Source/ObjectiveC2/README @@ -1,6 +1,6 @@ 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. -This code has been merged into the GNUstep Objective-C-2.0 runtime, which can -act as a drop-in replacement for GCC libobjc. You can find the code here: - -svn://svn.gna.org/svn/gnustep/libs/libobjc2/trunk +OS X 10.5 (and later) on top of the GCC Objective-C runtime. Much code has +been merged into the GNUstep Objective-C-2.0 runtime, which is used when +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 +APIs. diff --git a/Source/ObjectiveC2/weak.m b/Source/ObjectiveC2/weak.m new file mode 100644 index 0000000000..41340b6f5c --- /dev/null +++ b/Source/ObjectiveC2/weak.m @@ -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; +} + diff --git a/Tests/base/NSHashTable/weakObjects.m b/Tests/base/NSHashTable/weakObjects.m index 66ef8e6208..e47f10b202 100644 --- a/Tests/base/NSHashTable/weakObjects.m +++ b/Tests/base/NSHashTable/weakObjects.m @@ -1,25 +1,14 @@ #import "ObjectTesting.h" #import #import -#if __has_include() -#include -#endif int main() { START_SET("NSHashTable 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]; - NSHashTable *hashTable = [NSHashTable weakObjectsHashTable]; + NSAutoreleasePool *arp = [NSAutoreleasePool new]; + NSHashTable *hashTable = [NSHashTable weakObjectsHashTable]; - NSAutoreleasePool *arp2 = [NSAutoreleasePool new]; + NSAutoreleasePool *arp2 = [NSAutoreleasePool new]; id testObj = [[[NSObject alloc] init] autorelease]; [hashTable addObject:testObj]; diff --git a/Tests/base/NSMapTable/weakObjects.m b/Tests/base/NSMapTable/weakObjects.m index bc4c568584..7ce226a7a2 100644 --- a/Tests/base/NSMapTable/weakObjects.m +++ b/Tests/base/NSMapTable/weakObjects.m @@ -1,21 +1,10 @@ #import "ObjectTesting.h" #import #import -#if __has_include() -#include -#endif int main() { 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]; NSMapTable *mapTable = [NSMapTable strongToWeakObjectsMapTable]; diff --git a/Tests/base/NSPointerArray/weakObjects.m b/Tests/base/NSPointerArray/weakObjects.m index ed3458afdd..d79df8f923 100644 --- a/Tests/base/NSPointerArray/weakObjects.m +++ b/Tests/base/NSPointerArray/weakObjects.m @@ -1,21 +1,10 @@ #import "ObjectTesting.h" #import #import -#if __has_include() -#include -#endif int main() { 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]; NSPointerArray *array = [NSPointerArray weakObjectsPointerArray]; int index;