I need to store weak references to objects in an NSArray, in order to prevent retain cycles. I\'m not sure of the proper syntax to use. Is this the correct way?
As Jason said, you can't make NSArray
store weak references. The easiest way to implement Emile's suggestion of wrapping an object inside another object that stores a weak reference to it is the following:
NSValue *value = [NSValue valueWithNonretainedObject:myObj];
[array addObject:value];
Another option: a category that makes NSMutableArray
optionally store weak references.
Note that these are "unsafe unretained" references, not self-zeroing weak references. If the array is still around after the objects are deallocated, you'll have a bunch of junk pointers.
If you use a lot this comportment it's indicated to your own NSMutableArray class (subclass of NSMutableArray) which doesn't increase the retain count.
You should have something like this:
-(void)addObject:(NSObject *)object {
[self.collection addObject:[NSValue valueWithNonretainedObject:object]];
}
-(NSObject*) getObject:(NSUInteger)index {
NSValue *value = [self.collection objectAtIndex:index];
if (value.nonretainedObjectValue != nil) {
return value.nonretainedObjectValue;
}
//it's nice to clean the array if the referenced object was deallocated
[self.collection removeObjectAtIndex:index];
return nil;
}
If you do not require a specific order you could use NSMapTable
with special key/value options
NSPointerFunctionsWeakMemory
Uses weak read and write barriers appropriate for ARC or GC. Using NSPointerFunctionsWeakMemory object references will turn to NULL on last release.
To add weak self reference to NSMutableArray, create a custom class with a weak property as given below.
NSMutableArray *array = [NSMutableArray new];
Step 1: create a custom class
@interface DelegateRef : NSObject
@property(nonatomic, weak)id delegateWeakReference;
@end
Step 2: create a method to add self as weak reference to NSMutableArray. But here we add the DelegateRef object
-(void)addWeakRef:(id)ref
{
DelegateRef *delRef = [DelegateRef new];
[delRef setDelegateWeakReference:ref]
[array addObject:delRef];
}
Step 3: later on, if the property delegateWeakReference == nil
, the object can be removed from the array
The property will be nil, and the references will be deallocated at proper time independent of this array references
I believe the best solution for this is to use NSHashTable or NSMapTable. the Key or/and the Value can be weak. You can read more about it here: http://nshipster.com/nshashtable-and-nsmaptable/
I think an elegant solution is what Mr. Erik Ralston propose on his Github repository
https://gist.github.com/eralston/8010285
this are the essential steps:
create a category for NSArray and NSMutableArray
in the implementation create a convenience class with a weak property. Your category will assign the objects to this weak property.
.h
#import <Foundation/Foundation.h>
@interface NSArray(WeakArray)
- (__weak id)weakObjectForIndex:(NSUInteger)index;
-(id<NSFastEnumeration>)weakObjectsEnumerator;
@end
@interface NSMutableArray (FRSWeakArray)
-(void)addWeakObject:(id)object;
-(void)removeWeakObject:(id)object;
-(void)cleanWeakObjects;
@end
.m
#import "NSArray+WeakArray.h"
@interface WAArrayWeakPointer : NSObject
@property (nonatomic, weak) NSObject *object;
@end
@implementation WAArrayWeakPointer
@end
@implementation NSArray (WeakArray)
-(__weak id)weakObjectForIndex:(NSUInteger)index
{
WAArrayWeakPointer *ptr = [self objectAtIndex:index];
return ptr.object;
}
-(WAArrayWeakPointer *)weakPointerForObject:(id)object
{
for (WAArrayWeakPointer *ptr in self) {
if(ptr) {
if(ptr.object == object) {
return ptr;
}
}
}
return nil;
}
-(id<NSFastEnumeration>)weakObjectsEnumerator
{
NSMutableArray *enumerator = [[NSMutableArray alloc] init];
for (WAArrayWeakPointer *ptr in self) {
if(ptr && ptr.object) {
[enumerator addObject:ptr.object];
}
}
return enumerator;
}
@end
@implementation NSMutableArray (FRSWeakArray)
-(void)addWeakObject:(id)object
{
if(!object)
return;
WAArrayWeakPointer *ptr = [[WAArrayWeakPointer alloc] init];
ptr.object = object;
[self addObject:ptr];
[self cleanWeakObjects];
}
-(void)removeWeakObject:(id)object
{
if(!object)
return;
WAArrayWeakPointer *ptr = [self weakPointerForObject:object];
if(ptr) {
[self removeObject:ptr];
[self cleanWeakObjects];
}
}
-(void)cleanWeakObjects
{
NSMutableArray *toBeRemoved = [[NSMutableArray alloc] init];
for (WAArrayWeakPointer *ptr in self) {
if(ptr && !ptr.object) {
[toBeRemoved addObject:ptr];
}
}
for(WAArrayWeakPointer *ptr in toBeRemoved) {
[self removeObject:ptr];
}
}
@end