[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Problems with Replacing a String is a NSMutableString
From: |
David Chisnall |
Subject: |
Re: Problems with Replacing a String is a NSMutableString |
Date: |
Thu, 24 Jul 2008 09:41:35 +0100 |
On 24 Jul 2008, at 09:20, Charles philip Chan wrote:
So basically I am returning an array of the keys, and since there is
only 1 key for the object "Album" I search for it at index zero in the
array and return it as an NSMutableString. I then want to change the
string to the really key for the "Album" title and search the
dictionary
for the object- I am not even touching the dictionary. Also
"playingItem" is defined as mutable:
I think the problem is that you are misunderstanding the Objective-C
type system. The type of an object is a property of the object
itself, not of the label of the object (as it is in languages like C+
+, modulo structural typing).
When you insert an object into a dictionary, the key will be
immutable, the object may be mutable. When you call
allKeysForObject: you get an NSArray (immutable) containing pointers
to NSStrings (also immutable). You can not 'return it as an
NSMutableString,' you return it as an id, because that is what the
objectAtIndex: method on NSArray returns.
When you do something like this:
id a = {whatever};
NSMutableString *b = a;
You are not changing a into an NSMutableString, you are just
providing a hint to the compiler that you expect a to respond to any
messages declared on NSMutableString. If it does, at runtime,
possibly using forwarding, then this will work. If it doesn't, then
you will have problems, most likely runtime exceptions from
unrecognised selectors.
You can not modify the key of an item in an NSDictionary, because
that would alter the mappings without NSDictionary knowing that they
had altered. When you later did objectForKey: on the dictionary,
NSDictionary would call -hash on the key you passed in. It would
then look in the bucket associated with this hash and fail to find
the key, because it put the key in the bucket corresponding to the
return value of -hash when it was entered. The documentation on
NSObject tells you that the return value from -hash may not change
when an object is in a collection.
Looking at your code, it seems that you don't actually want to change
the key in the dictionary, you want to change a copy of it. The
easiest way to do this is to just send a mutableCopy message, like this:
albumIndexTemp = [[[playingItem allKeysForObject:@"Album"]
objectAtIndex: 0] mutableCopy];
This will not modify the dictionary state, and will give you an
NSMutableString you can do things with.
For tracking down this kind of bug (and ensuring it doesn't get
reintroduced), you might find this macro that I use with Étoilé useful:
#ifdef DEBUG
#define SAFECAST(type, obj) ([obj isKindOfClass:[type class]] \
? (type*)obj \
: ([NSException raise:@"InvalidCast"\
format:@"Can not cast %@ to %s", obj,\
#type], (type*)nil))
#else
#define SAFECAST(type, obj) (type*)obj
#endif
Use it like this:
albumIndexTemp = SAFECAST(NSMutableString, [[playingItem
allKeysForObject:@"Album"]
objectAtIndex: 0]);
If you compile in debug mode, this will insert an -isKindOfClass:
test and throw an exception (which you can find in the debugger
easily) if you are trying to cast an object pointer to an incorrect
type. If you compile in release mode then you will get a pointer
cast, which has no run time overhead.
David