Программирование на Objective-C 2.0
Шрифт:
Поскольку тип объекта, хранящегося в переменной id, во время компиляции может быть неизвестен, некоторые проверки откладываются до выполнения программы (runtime). Рассмотрим следующую последовательность кодов. Fraction *f1 = [[Fraction alloc] init]; [f1 setReal: 10.0 andlmaginary: 2.5];
Поскольку метод setReakandlmaginary: применяется к комплексным числам, а не к дробям, при компиляции программы, содержащей эти строки, появится предупреждающее сообщение. ргодЗ.т: In function 'main':(B функции 'main') prog3.m:13: warning: ’Fraction' does not respond to 'setReakandlmaginary:' (предупреждение: объект типа 'Fraction'
Компилятору Objective-C известно, что И является объектом класса Fraction, поскольку он был объявлен именно так. При появлении выражения с сообщением [Н setReal: 10.0 andlmaginary: 2.5];
ему стало известно, что класс Fraction не содержит метода setReal:andlmaginary: (и не наследует его). Поэтому компилятор выдает предупреждающее сообщение.
Теперь рассмотрим следующую последовательность кодов. id dataValue = [[Fraction alloc] init]; [dataValue setReal: 10.0 andlmaginary: 2.5];
Компилятор не выводит предупреждающего сообщения, поскольку во время обработки исходного файла ему неизвестно, какой тип объекта сохраняется в dataValue.
Сообщение об ошибке не появится, пока вы не запустите программу, содержащую эти строки. Сообщение об ошибке может выглядеть следующим образом. objc: Fraction: does not recognize selector -setReakandlmaginary: (не распознается селектор -setReakandlmaginary) dynamic3: received signal: Abort trap (получен сигнал: аварийное прерывание) When attempting to execute the expression (При попытке выполнить выражение) [dataValue setReal: 10.0 and I maginary: 2.5];
Система runtime сначала проверяет тип объекта, хранящегося внутри dataValue. Поскольку dataValue содержит дробь (объект Fraction), система runtime проверяет, определен ли метод setReal:andlmaginary: для класса Fraction. Поскольку это не так, выдается сообщение, и выполнение программы прекращается. 9.4. Тип данных id и статический контроль типов
Если тип данных id можно использовать для хранения любого объекта, так почему бы нам не объявлять все объекты с типом id? Этого не следует делать по нескольким причинам.
Во-первых, определяя переменную как объект из определенного класса, мы используем так называемый статический контроль типов (static typing). Слово статический означает, что переменная всегда используется для хранения объектов из определенного класса, поэтому класс объекта, хранящегося в этом типе, заранее определен, то есть является статическим. При использовании статического контроля типов компилятор обеспечивает согласованное использование этой переменной во всей программе. Компилятор может проверить, определен ли (или унаследован) метод, применяемый к объекту, и если нет, то выводит предупреждающее сообщение. Таким образом, если вы объявляете в своей программе переменную класса Rectangle с именем myRect, то компилятор проверяет, все ли методы, которые вы вызываете для myRect, определены в классе Rectangle или наследуются из его суперкласса.
Примечание. Определенные приемы позволяют вызывать методы, который указываются самой переменной, в таком случае компилятор не может выполнить проверку.
Но если проверка будет выполнена во время выполнения, почему вас должен интересовать статический контроль типов? Дело в том, что лучше выявить ошибки на
Еще одним доводом к применению статического контроля типов является то, что он делает ваши программы более удобными для чтения. Рассмотрим объявление id f1;
и сравним его с Fraction *f1;
Конечно, второе объявление лучше, поскольку в нем указывается предполагаемое использование переменной И. Сочетание статического контроля типов и осмысленных имен переменных позволяет делать программы самодоку- ментируемыми. Типы аргументов и возвращаемых значений при динамическом контроле типов
Если для вызова метода используется динамический контроль типов, соблюдайте следующее правило. Если методы с одинаковым именем реализованы в нескольких классах, каждый метод должен быть согласован с типом каждого аргумента и типом возвращаемого значения, чтобы компилятор мог генерировать правильный код для выражений с сообщениями.
Компилятор выполняет проверку на согласованность в объявлениях каждого класса, которые он встречает. Если один или несколько методов не согласуются с типом аргумента или возвращаемого значения, компилятор выводит предупреждающее сообщение. Например, оба класса, Fraction и Complex, содержат метод add:, но класс Fraction принимает в качестве аргумента и возвращает объект типа Fraction, а класс Complex - объект типа Complex. Если trad и myFract - объекты типа Fraction, a compl и myComplex — объекты типа Complex, то определения result = [myFract add: trad];
и result = [myComplex add: compl];
не вызовут проблемы. В обоих случаях получатель сообщения доступен для статического контроля типов и компилятор может проверить согласованность при использовании метода, поскольку он определен в классе получателя. Если dataValuel и dataValue2 — переменные типа id, то выражение result = [dataValuel add: dataValue2];
заставляет компилятор генерировать код для передачи данного аргумента методу add: и обработки его возвращаемого значения, делая некоторые предположения.
Во время выполнения система runtime Objective-C проверит конкретный класс объекта, хранящегося в dataValuel, и выберет метод из подходящего класса для выполнения. Однако в более общем случае компилятор может генерировать неверный код для передачи методу аргументов или обработки его возвращаемого значения. Это может произойти, например, если один метод принял в качестве аргумента какой-либо объект, а другой — значение с плавающей точкой, или один метод возвратил объект, а другой — целое значение. Если методы отличаются только типом объекта (например, метод add: класса Fraction принимает в качестве аргумента и возвращает объект типа Fraction, а метод add: класса Complex принимает в качестве аргумента и возвращает объект типа Complex), то компилятор же будет генерировать правильный код, поскольку адреса памяти (то есть указатели) в обоих случаях будут передаваться как ссылки на объекты. 9.5. Как задавать вопросы о классах