discuss-gnustep
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]