Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-retaining array for delegates

In a Cocoa Touch project, I need a specific class to have not only a single delegate object, but many of them.

It looks like I should create an NSArray for these delegates; the problem is that NSArray would have all these delegates retained, which it shouldn't (by convention objects should not retain their delegates).

Should I write my own array class to prevent retaining or are there simpler methods? Thank you!

like image 659
wh1t3cat1k Avatar asked Jan 14 '11 14:01

wh1t3cat1k


2 Answers

I found this bit of code awhile ago (can't remember who to attribute it to).

It's quite ingenius, using a Category to allow the creation of a mutable array that does no retain/release by backing it with a CFArray with proper callbacks.

@implementation NSMutableArray (WeakReferences)     + (id)mutableArrayUsingWeakReferences {     return [self mutableArrayUsingWeakReferencesWithCapacity:0];     }      + (id)mutableArrayUsingWeakReferencesWithCapacity:(NSUInteger)capacity {     CFArrayCallBacks callbacks = {0, NULL, NULL, CFCopyDescription, CFEqual};     // We create a weak reference array     return (id)(CFArrayCreateMutable(0, capacity, &callbacks));     } @end 

EDIT Found the original article: http://ofcodeandmen.poltras.com

like image 189
MarkPowell Avatar answered Oct 05 '22 14:10

MarkPowell


I am presenting an important limitation of one of the earlier answers, along with an explanation and an improvement.

Johnmph suggested using [NSValue valueWithNonretainedObject:].

Note that when you do this, your reference acts not like __weak, but rather like __unsafe_unretained while inside the NSValue object. More specifically, when you try to get your reference back (using [myNSValue nonretainedObjectValue]), your application will crash with an EXC_BAD_ACCESS signal if the object has been deallocated before that time!

In other words, the weak reference is not automatically set to nil while inside the NSValue object. This took me a bunch of hours to figure out. I have worked around this by creating a simple class with only a weak ref property.

More beautifully, by using NSProxy, we can treat the wrapper object entirely as if it is the contained object itself!

// WeakRef.h @interface WeakRef : NSProxy  @property (weak) id ref; - (id)initWithObject:(id)object;  @end   // WeakRef.m @implementation WeakRef  - (id)initWithObject:(id)object {     self.ref = object;     return self; }  - (void)forwardInvocation:(NSInvocation *)invocation {     invocation.target = self.ref;     [invocation invoke]; }  - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {     return [self.ref methodSignatureForSelector:sel]; }  @end 
like image 44
Timo Avatar answered Oct 05 '22 16:10

Timo