Re: NSDictionary design bug (was: Re: Ugly bug in Foundation, beware!)
Re: NSDictionary design bug (was: Re: Ugly bug in Foundation, beware!)
- Subject: Re: NSDictionary design bug (was: Re: Ugly bug in Foundation, beware!)
- From: Christian Brunschen <email@hidden>
- Date: Mon, 7 Jun 2004 21:13:06 +0200 (MEST)
There's been a lot of debate on this issue; I'm going to be horrible and
actually propose a change that might make everyone a bit happier!
Basically there are two approaches being discussed:
1) Just Reference The Key
This is the approach used by many other dictionary / HashTable
implementations. It is conceptually the easiest, but it means that the
developer has to manually handle the possibility that one of the keys
might mutate behind their back, thus confusing the poor NSDictionary.
The advantage of this approach is that it is simple and has predictable
results, and easily predictable issues.
The disadvantage is that the predictable issues may not be so easy to
avoind or resolve; if a developer wants to use an arbitrary object as the
key in a dictionary, there's no easy way they can be sure that the object
won't mutate. This is true in particular when using third-party frameworks
(or even Apple's frameworks) which might return a mutable object for
performance reasons or similar. This might lead developers to start
sending 'copy' messages to any objects they want to use as the key in a
dictionary, with the hope that the copy is going to be a) immutable, or at
least b) not referencced by anyone or anything else, so even it may be
mutable, won't actually be mutated.
2) Copy The Key
This approach is the one that NSDictionary uses, and would seem to be a
simple extrapolation after discovering the "I'd best make sure the keys
are immutable, so I'll just copy them" issue - by moving the copying into
the NSDictionary itself, one gets rid of lots of repetitive and ugly code
in setObject:forKey: message-sends.
This has the advantage of basically working, as long as the objects used
as keys:
a) implement the NSCopying protocol
b) the copy will always compare equal to its original
c) the copy is in fact immutable
But this approach also has the drawbacks that are being discussed: it
places those requirements mentioned above on any objects we would want to
use as keys in a dictionary, it may end up creating lots of redundant
copies, and so on.
So I am going to suggest a third approach.
3) Let The Object Decide
This third approach realizes that both the approaches above have their
advantages and drawbacks, and that it wuold be nice if there was something
that was even better; and I think I have found such an approach.
This approach is one which is actually already used in other parts of
Cocoa. The NSCoding protocol, for instance, includes a method
'-replacementObjectForCoder:' which allows one object, when asked to
encode itself, to offer another object to be encoded in its place.
By introducing a new protocol, I'll call it 'NSDictionaryKey' for this
discussion, each object could be asked for the correct behaviour - whether
to use the object directly, whether the object needs to be copied, and so
on. Something like this:
@protocol NSDictionaryKey
- (id) replacementKeyForDictionary:(NSDictionary *)dict;
@end
This would allow any class to specify the behaviour it needed.
NSMutableString would implement
- (id) replacementKeyForDictionary:(NSDictionary *)dict {
return [[self copy] autorelease];
}
NString could do
- (id) replacementKeyForDictionary:(NSDictionary *)dict {
return self;
}
... and any class of the developer's own devising could do exactly what it
wanted. The object returned by 'replacementKeyForDictionary:' would still
have to obey the rules about being equal to the object it is a replacement
for; i.e., it would still have to return the same value from -hash, and
'[[foo replacementKeyForDictionary:dict] isEqual:foo]' would have to
return YES.
The nice thing about this solution is that it can actually be retrofitted
onto the existing Cocoa frameworks without breaking anything. This would
entail:
1) Changing NSDictionary from using [key copy] to using
[key replacementKeyForDictionary:self] in '-setObject:forKey:'
2) Adding a defaultimplementation of
'- (id) replacementKeyForDictionary:(NSDictionary *)dict' to NSObject:
- (id) replacementKeyForDictionary:(NSDictionary *)dict {
if ([self conformsToProtocol:@protocol(NSCopying)]) {
return [self copy];
} else {
return self;
}
}
By adding this method to NSObject, all subclasses of NSObject would
inherit this method and would continue to work as before, as this would
make all objects that conform to the NSCopying protocol use, by default, a
copy of themselves as the replacement key. This duplicates the behaviour
of the current implementation, so everything would continue to work
undisturbed.
At the same time, it would give class developers a well-defined extension
point where they can make certain that their classes implement the
appropriate behaviour, while also *not* overloading the '-copy' method
(and this the NSCopying protocol) to have a special meaning for objects
that are intended to be used as keys in NSDictionaries.
So this approach would avoid to the disadvantages of both the first and
second approaches: There would be no redundant code written over and over,
and there would be a well-documented way of making sure that developers
can make their classes suitable for being used as NSDictionary keys. And
all of this can be implemented in a backwards-compatible manner.
So basically, my suggestion is that implementing something like the third
approach above would essentially resolve the issue in a way that should
make everybody reasonably happy: It would remove the automatic copying of
keys, and it would allow each class, indeed each individual object, to
specify another key to use in its place if warranted - specifically for
those cases where someone attempts to use a mutable object as a key in a
dictionary. And all while not breaking any existing code.
If you've managed to read to the end of this post, then I hope I haven't
bored you too badly :) Please do feel free to comment on the above. Keep
in mind that this is just typed directly into this email from my somewhat
disorganized mind, so there are bound to be errors, and I'm glad to have
them identified and pointed out, so they can be corrected.
Best wishes,
// Christian Brunschen
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.