Динамическая типизация и возвращаемые значения в Objective-C - PullRequest
4 голосов
/ 11 июля 2009

Я столкнулся с очень странным поведением, которое не могу понять. У меня есть Texture класс с contentWidth свойством типа int. Этот класс обернут в класс Image, который имеет свойство width типа int. width Image вычисляется просто как contentWidth базовой текстуры:

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

Теперь Image используется классом Button (по составу, а не по наследству), который хочет прочитать размер изображения:

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

Проблема в том, что переменная height устанавливается очень хорошо, тогда как переменная width содержит NaN (–2147483648). Я проверил размеры - они примерно 200 × 100 или около того, нигде рядом с пределом int. Также подсказка отладчика XCode показывает оба свойства в Texture правильно, только после того, как ширина проходит через два метода доступа, число искажается. Объекты не освобождаются. Чего мне не хватает?


Обновление: Я расширил метод доступа в классе Image, чтобы увидеть, откуда возникла проблема:

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

Теперь, когда я разбиваю первую строку, w устанавливается правильно. Я перехожу, выполнение возвращается к вызывающей функции:

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

… и теперь bar содержит NaN.


Обновление: Ммм, получил:

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

image объявляется как id, что и здесь. Может кто-нибудь объяснить это? У меня есть ощущение, что я не вызываю правильный метод width, но что именно здесь происходит? Я всегда печатаю свои переменные настолько строго, насколько это возможно, но здесь тип id удобен. Я понятия не имел, что это может привести к такой ошибке.

Ответы [ 2 ]

5 голосов
/ 11 июля 2009

Ну хорошо. Если вас интересует, почему происходит такая ситуация, см. Руководство по программированию Objective-C, глава 8, Типы возврата и аргумента :

В общем, методы разные классы с одинаковым селектором (то же имя) должны также поделиться одни и те же типы возврата и аргумента. это ограничение накладывается компилятором разрешить динамическое связывание. Поскольку класс получателя сообщения (и поэтому специфичные для класса детали о метод, который предлагается выполнить), не может быть известно во время компиляции, компилятор должен обработать все методы одно и то же имя. Когда он готовится информация о методе возврата и типы аргументов для системы времени выполнения, это создает только одно описание метода для каждого селектора метода.

Однако, когда сообщение отправлено статически типизированный объект, класс получатель известен компилятору. Компилятор имеет доступ к специфичная для класса информация о методы. Поэтому сообщение освобожден от ограничений на его типы возвращаемого значения и аргумента.

Вот сообщение об ошибке в bugzilla GCC (и соответствующее сообщение в блоге ). Поведение не является ошибкой, но оно не очень дружелюбно, потому что компилятор не предупреждает вас (по крайней мере, с настройками предупреждений по умолчанию).

1 голос
/ 11 июля 2009

Разве ваш аксессор не должен быть:

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

Является ли текстура переменной экземпляра? Если это так, вы сделали super для инициализации в методе init или в своем классе-обертке?

...