Reputation: 6049
Specifically:
self.words = (NSMutableArray*)[self.text componentsSeparatedByString:@" "];
works fine so long as there are separators. I see that the method returns the original string wrapped in an NSArray if there isn't, though. This single element NSArray stubbornly refuses to be cast as an NSMutableArray.
But, when I do:
self.words = [NSMutableArray arrayWithArray:self.words];
It works just fine.
Is there something I'm missing here? Is it bad practice to cast from NSArray to NSMutableArray?
Upvotes: 5
Views: 969
Reputation: 27133
Use second variant in all cases because it is right solution and more clearly for users who will support your code:
NSArray *array = [self.text componentsSeparatedByString:@" "];
self.words = [NSMutableArray arrayWithArray:array];
Never do this one:
self.words = [NSMutableArray arrayWithArray:self.words];
It will totally confuse all devs =)
Upvotes: 1
Reputation: 31745
NSString* string1 = @"this thing";
NSString* string2 = @"this";
NSMutableArray* array1;
NSMutableArray* array2;
array1 = (NSMutableArray*)[string1 componentsSeparatedByString:@" "];
array2 = (NSMutableArray*)[string2 componentsSeparatedByString:@" "];
[array1 addObject:string1]; //(A)
[array2 addObject:string1]; //(B)
This will break at (B) the last line with :
-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x1702257a0
But will not break at (A)1
An object that is publicly declared as immutable may have been privately created as mutable. In this case, if you cast from the immutable public type to the private mutable type, everything will work fine. Where the object was not created as mutable, such a cast will not get you what you want.
In the case of componentsSerparatedByString
, this suggests that the method implementation creates a mutable array only if it needs to - i.e. if it has to add more than one object to the array. If it only finds one object, you get an NSArray, if it finds more than one, you get an NSMutableArray. This is an implementation detail that is deliberately hidden from you as the user.
The interface tells you to expect an NSArray in all cases, and in all cases this will work.
You should not rely on such details to get you what you want. Stick to the public API, that's what it is for.
1 rather, as Bryan Chen points out, it may not break now but could well do so in the future
Upvotes: 2
Reputation: 21808
When you do that:
self.words = (NSMutableArray*)[self.text componentsSeparatedByString:@" "];
the array still remains immutable and if you send it a message from NSMutableArray
class it will crash. The trick (NSMutableArray*)
is only good to make the compiler happy.
In the second case:
self.words = [NSMutableArray arrayWithArray:self.words];
you do not CAST, you CREATE a new array object.
And of course you don't need to cast the arrays this way. NSMutableArray
object IS already NSArray
just like any object of a derived class is an object of a base class at the same time
Upvotes: 1
Reputation: 503
Casting does nothing with an object. example:
NSString *mouse = (id)@[@"mouse"];
it will compile, but variable mouse is not NSString. it is NSArray. you can check it simply by writing
po mouse
in console.
The only way to create mutable copy of an object is to call 'mutableCopy' method on it:
NSArray *array = @[@"a"];
NSMutableArray *mutableCopy = [array mutableCopy];
Upvotes: 0
Reputation: 131408
You are confused.
This code:
self.words = (NSMutableArray*)[self.text componentsSeparatedByString:@" "];
...is wrong, and is setting you up for a crash later on. Casting just tells the compiler "trust me, I know what I'm doing." It does not change the type of the underlying object. The method componentsSeparatedByString returns an NSArray, not a mutable array. If you then try to mutate the resulting array, you will crash with an unrecognized selector message. With the cast, the compiler trusts you that your object will really be a mutable array at runtime, but it will not be.
This would crash:
self.words = (NSMutableArray*)[self.text componentsSeparatedByString:@" "];
[self.words addObject: @"new"];
However, this code:
self.words = [[self.text componentsSeparatedByString:@" "] mutableCopy];
[self.words addObject: @"new"];
...does the right thing. It doesn't cast a pointer, it is a method call to a method that takes an NSArray as input and returns a mutable array with the same contents. Thus the second line will work because the first line takes the immutable array it gets back from componentsSeparatedByString and uses it to create a mutable array.
Upvotes: 4
Reputation: 41801
Your misconception seems to be about the nature of casting. Casting doesn't change the object, it just tells the compiler to pretend that it's an object of that type. If the object really isn't an NSMutableArray, casting is not expected to make it become one.
Upvotes: 2
Reputation: 70946
Is it bad practice to cast from NSArray to NSMutableArray?
Yes. Bordering on nonsensical, really.
Typecasting does not change the object in any way at all, it just tells the compiler that it should regard the object as if it were an instance of the new type. At run time though, the object stays exactly the same. You may cast an NSArray
to an NSMutableArray
but it doesn't make the object transform into an NSMutableArray
. It's still the same object of the same type.
Upvotes: 2
Reputation: 8200
In the first case, the componentsSeparatedByString:
method is specifically returning an NSArray
, which can't just be cast to the mutable type. If you wanted that to be mutable, you would have to do this:
self.words = [[self.text componentsSeparatedByString:@" "] mutableCopy];
The second one is calling the arrayWithArray:
method on the NSMutableArray
class, meaning it is making an instance of NSMutableArray
. That's why it works. You can cast an NSMutableArray
to an NSArray
, but not the other way around, because an NSMutableArray
is a subclass of NSArray
.
Upvotes: 0
Reputation: 46588
It is bad practice to cast from NSArray
to NSMutableArray
. It may works if you are lucky because the array are constructed using NSMutableArray
, but you can't rely on it.
If you want NSMutableArray
from NSArray
, use mutableCopy
self.words = [[self.text componentsSeparatedByString:@" "] mutableCopy];
Upvotes: 2