问题
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!
回答1:
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
回答2:
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
回答3:
Check documentation of NSValue valueWithNonretainedObject method :
This method is useful for preventing an object from being retained when it’s added to a collection object (such as an instance of NSArray or NSDictionary).
回答4:
I'd suggest to not-fight-the-framework and use NSPointerArray with the NSPointerFunctionsWeakMemory
NSPointerFunctionOption
like this:
NSPointerArray *weakReferencingArray = [NSPointerArray pointerArrayWithOptions:NSPointerFunctionsWeakMemory];
// NSPointerFunctionsWeakMemory - Uses weak read and write barriers
// appropriate for ARC or GC. Using NSPointerFunctionsWeakMemory
// object references will turn to NULL on last release.
Served me well in scenarios, where I had to design a delegates array, which auto-NULL's references.
回答5:
You do not want to do this! Cocoa Touch have several concepts for sending events, you should use the proper concept for each case.
- Target-action: For UI controls, such as button presses. One sender, zero or more receivers.
- Delegates: For one sender and one receiver only.
- Notification: For one sender, and zero or more receivers.
- KVO: More fine grained that notifications.
What you should do is to look into how to use NSNotificationCenter
class. This is the proper way to send a notification that have more than one receiver.
回答6:
This one from NIMBUS would be more simple:
NSMutableArray* NICreateNonRetainingMutableArray(void) {
return (NSMutableArray *)CFArrayCreateMutable(nil, 0, nil);
}
NSMutableDictionary* NICreateNonRetainingMutableDictionary(void) {
return (NSMutableDictionary *)CFDictionaryCreateMutable(nil, 0, nil, nil);
}
NSMutableSet* NICreateNonRetainingMutableSet(void) {
return (NSMutableSet *)CFSetCreateMutable(nil, 0, nil);
}
回答7:
I found some pieces of code from Three20 project about this topic, i hope this helps...
NSMutableArray* TTCreateNonRetainingArray() {
CFArrayCallBacks callbacks = kCFTypeArrayCallBacks;
callbacks.retain = TTRetainNoOp;
callbacks.release = TTReleaseNoOp;
return (NSMutableArray*)CFArrayCreateMutable(nil, 0, &callbacks);
}
NSMutableDictionary* TTCreateNonRetainingDictionary() {
CFDictionaryKeyCallBacks keyCallbacks = kCFTypeDictionaryKeyCallBacks;
CFDictionaryValueCallBacks callbacks = kCFTypeDictionaryValueCallBacks;
callbacks.retain = TTRetainNoOp;
callbacks.release = TTReleaseNoOp;
return (NSMutableDictionary*)CFDictionaryCreateMutable(nil, 0, &keyCallbacks, &callbacks);
}
回答8:
Keyword: NSHashTable
, search in documentations.
回答9:
I found an open source library named XMPPFramewrok
There is a multicast delegate solution in the project
https://github.com/robbiehanson/XMPPFramework/wiki/MulticastDelegate
回答10:
What about storing in the array or dictionary
__weak typeof(pointer) weakPointer = pointer;
来源:https://stackoverflow.com/questions/4692161/non-retaining-array-for-delegates