Search code examples
iosobjective-ccastingnsmutablearrayincompatibility

Objective-C: Uncast element of NSMutableArray returns unexpected type


If am having trouble understanding the following. Consider the following code:

UIImage *image = [[UIImage alloc] initWithContentsOfFile:@"car.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:imageView];

UIImage *image2 = [imageView image];
UIImage *image3 = [[array objectAtIndex:0] image];
UIImage *image4 = [(UIImageView *) [array objectAtIndex:0] image];

The instructions with image2 and image4 work as expected. The line with image3 on the other hand displays an issue,

Incompatible pointer types initializing 'UIImage *' with an expression of type 'CIImage *'

I understand not type-casting the object retrieved from NSMutableArray might cause a problem. However, I have a hard time understanding why the compiler would think this expression to be of type CIImage.

If this question has been asked in more general terms, I apologize, I couldn't find it...


Solution

  • Let's pull this apart:

    UIImage *image3 = [[array objectAtIndex:0] image];
    

    Just look at this part:

    [array objectAtIndex:0] 
    

    Objective-C has no type information for the object returned from this method call, so it types it as an id. Now you are asking to send the image message to an id. But an id is not a typed object. What is Objective-C to make of this?

    Basically, it has to guess what message you are sending. So it looks thru its known repertory of image methods, and just picks one. It knows of six such methods:

    enter image description here

    You meant the fourth one. But it happens that the compiler picks the first one, which returns a CIImage. So by your standards, it guessed "wrong". But you made it guess, and it had to guess something.

    This is exactly the problem that the cast in your last example fixes:

    UIImage *image4 = [(UIImageView *) [array objectAtIndex:0] image];
    

    So, the moral: don't do what you did in the image3 line. Do what you did in the image4 line! You have type information that the compiler doesn't have: tell the compiler what you know.

    (Observe that no harm is actually done by the way you wrote the code. The compiler warning is just that: a warning. In actual fact, when the image message is sent to the UIImageView, a UIImage will be returned and all will be well. But the compiler is warning you that it doesn't know this. By not casting you have given up all static typing and forced the compiler to take its hands off. The compiler doesn't know what will happen, so it warns you that this will have to be resolved at runtime. When you cast, you resolve the matter at compile time.)