Застрял в понимании динамического связывания в Objective-C - PullRequest
3 голосов
/ 06 ноября 2011

Я только начал изучать Objective-C, я читаю Программирование в Objective-C, 3-е издание Стивен Г. Кочан .

Есть параграф, объясняющий механизм полиморфизма:

Во время выполнения система времени выполнения Objective C проверит фактический класс объекта, хранящийся в dataValue1 (объект id), и выберет подходящий метод из правильного класса для выполнения. Однако в более общем случае компилятор может сгенерировать неправильный код для передачи аргументов методу или обработки его возвращаемого значения. Это произошло бы, если бы один метод принял объект в качестве своего аргумент, а другой принял значение с плавающей точкой, например. Или же например, если один метод вернул объект, а другой - целое число. Если несоответствие между двумя методами представляет собой просто объект другого типа (например, метод Fraction's add: метод принимает объект Fraction в качестве аргумента и возвращает его, а метод add: метод Complex принимает и возвращает объект Complex), компилятор будет по-прежнему генерировать правильный код, потому что адреса памяти (то есть указатели) все равно передаются как ссылки на объекты.

Я не совсем понимаю, что в первой части параграфа говорится, что компилятор может генерировать неправильный код, если я объявлю 2 метода в разных классах с одинаковым именем и аргументами другого типа. в то время как последняя часть параграфа говорит, что хорошо иметь 2 метода с одинаковыми именами, разными аргументами и типами возвращаемых данных ... о нет ...

У меня есть следующий код, и они компилируются и работают нормально:

@implementation A
- (int) add:(int)a {
    return 1 + a;
}
@end
@implementation B
- (int) add: (B*) b {
    return 100;
}
@end
id a = [[A alloc] init];
id b = [[B alloc] init];
NSLog(@"A: %i, B %i", [a add:100], [b add:b]);

Edit: Как и в приведенном мною тексте, приведенный выше код должен вызывать ошибки, но он генерирует только некоторые предупреждающие сообщения: Несколько методов с именем "add:" found , Несовместимый указатель на целочисленное преобразование, отправляющий "id" к параметру типа "int"

У меня есть опыт работы с Java и C ++, я знаю, что полиморфизм в Objective-C немного отличается от полиморфизма этих языков, но я все еще не уверен в неопределенности (текст выделен жирным шрифтом).

Я думаю, что, должно быть, что-то неправильно понял, не могли бы вы объяснить более подробно о динамическом связывании в Objective-C для меня и тех, кому это нужно?

Спасибо тебе!

Ответы [ 2 ]

6 голосов
/ 06 ноября 2011

Вы не заметили ничего необычного, потому что эти два метода имеют одинаковую семантику вызова, например, в ABI x86_64. Указатели можно считать целыми числами, и в ABI x86_64 они передаются целевому методу таким же образом.

Однако, если у вас был другой класс, например ::1003

@implementation C
- (int)add:(float)number {
    return (int)number + 100;
}
@end

получает аргумент с плавающей точкой (как упомянуто Кочаном), затем компилятор при разборе:

id a = [[A alloc] init];
id b = [[B alloc] init];
id c = [[C alloc] init];
NSLog(@"A: %i, B %i, C %i", [a add:100], [b add:b], [c add:100]);

не знал бы, что для [c add:100] он должен поместить 100 в регистр с плавающей запятой, как указано в x86_64 ABI. Следовательно, -[C add:], который ожидает, что аргумент с плавающей точкой находится в регистре с плавающей точкой, считывает значение, которое не соответствует аргументу 100.

Чтобы это работало, вам нужно было бы либо объявить переменную со статическим типом:

C *c = [[C alloc] init];

или приведите его к правильному типу при отправке сообщения:

[(C *)c add:100];

В конце дня отправка сообщения Objective C является вызовом функции. Разные ABI могут иметь разную семантику для вызова функций с переменными аргументами, аргументами с плавающей точкой или целочисленными аргументами или возвращаемыми значениями или структурами вместо скалярных арифметических типов. Если компилятор видит разные сигнатуры методов, которые обрабатываются по-разному в соответствии с целевым ABI, и если недостаточно информации о типе, он может в итоге выбрать неправильную сигнатуру метода.

3 голосов
/ 06 ноября 2011

Различие сделано потому, что в последнем случае разница только в классе аргументов. Complex* и Fraction* являются указателями, поэтому, даже если между двумя методами с одинаковыми именами существует путаница, проблем нет.

Ситуация в вашем примере, с другой стороны, опасна, поскольку один аргумент является указателем, а другой - int. Быть в безопасности это легко, однако:

NSLog(@"A: %i, B %i", [(A*)a add:100], [(B*)b add:b]);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...