views:

611

answers:

2

Hello!

I have run into a very strange behaviour I can’t make sense of. I have a Texture class with contentWidth property of type int. This class is wrapped in a Image class that has a width property of type int. The width of an Image is computed simply as the contentWidth of the underlying texture:

- (int) width
{
    return texture.contentWidth;
}

Now the Image is used by a Button class (by composition, not inheritance) that wants to read the image size:

// image is of type ‘id’
int width = [image width];
int height = [image height];

The problem is that the height variable gets set just fine, whereas the width variable contains NaN (–2147483648). I have checked the dimensions – they are about 200×100 or so, nowhere near the int limit. Also the Xcode debugger tooltip shows both properties in the Texture correctly, only after the width gets through the two accessors the number gets garbled. The objects are not freed. What am I missing?


Update: I have expanded the accessor in the Image class to see where the problem originates:

- (int) width
{
    const int w = texture.contentWidth;
    return w;
}

Now when I break on the first line, the w gets set correctly. I step over, the execution returns to the calling function:

- (void) foo
{
    int bar = [image width];
}

…and now the bar contains NaN.


Update: Mhm, got it:

int foo = [image width]; // image is id, returns NaN
int bar = [(Image*) image width]; // correct value

The image is declared as id, which is the point here. Could somebody explain this? I have got a feeling I am not calling the right width method, but what exactly is going on here? I always type my variables as strictly as possible, but here the id type is handy. I had no idea it could result in such a bug.

+1  A: 

Shouldn't your accessor be:

- (int) width
{
    return [texture contentWidth];
}

Is texture an instance variable? If it is, did you make super to initialize it in the init method or your wrapper class?

micmoo
The two are equivalent - Objective-C will convert the dot-accessor syntax to a passed message during compilation.
Tim
Its a good idea anyway from a "good practice" standpoint... In that case, I would make sure your instance is properly initialized.
micmoo
I have just overriden the ‘description’ method on the Texture class and logged the dimensions there, they are fine.
zoul
+4  A: 

Oh well. If you are interested in why this situation happens, see the Objective-C Programming Guide, chapter 8, Return and Argument Types:

In general, methods in different classes that have the same selector (the same name) must also share the same return and argument types. This constraint is imposed by the compiler to allow dynamic binding. Because the class of a message receiver (and therefore class-specific details about the method it’s asked to perform), can’t be known at compile time, the compiler must treat all methods with the same name alike. When it prepares information on method return and argument types for the runtime system, it creates just one method description for each method selector.

However, when a message is sent to a statically typed object, the class of the receiver is known by the compiler. The compiler has access to class-specific information about the methods. Therefore, the message is freed from the restrictions on its return and argument types.

Here’s a bug report in the GCC bugzilla (and a corresponding blog post). The behaviour is not a bug, but it isn’t very friendly either, because the compiler does not warn you (at least not with the default warning settings).

zoul