Программирование на Objective-C 2.0
Шрифт:
Примечание. Определенные приемы позволяют вызывать методы, который указываются самой переменной, в таком случае компилятор не может выполнить проверку.
Но если проверка будет выполнена во время выполнения, почему вас должен интересовать статический контроль типов? Дело в том, что лучше выявить ошибки на этапе компиляции программы, чем на этапе выполнения. Если программу будет выполнять другой человек, вы не сможете увидеть ошибку. Это означает, что если программа введена в эксплуатацию, ничего не подозревающий пользователь может столкнуться с ошибкой, при которой указывается, что определенный объект не распознает некоторый метод.
Еще одним доводом к применению статического контроля
и сравним его с 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. Как задавать вопросы о классах
При работе с переменными, которые могут содержать объекты из различных классов, вам могут потребоваться ответы на вопросы следующего рода.
Является ли данный объект прямоугольником (rectangle)?
Поддерживает ли данный объект метод print?
Является ли данный объект членом класса Graphics или одного из его потомков?
Ответы на такие вопросы можно использовать, чтобы выполнять разные последовательности кода, чтобы избежать возникновения ошибок или проверить целостность программы во время ее выполнения.
В таблице 9.1 приводится сводка основных
методов, поддерживаемых классом Object. В таблице объект-класс (object-class) — это объект, получаемый для заданного класса (обычно он генерируется с помощью метода class), а с е л е к т о р (s e le c to r ) — это значение типа SEL (обычно создается с помощью директивы @selector).Табл. 9.1. Методы для работы с динамическими типами Метод Вопрос или действие -(BOOL) isKindOfClass: объект-класс Является ли объект членом о б ъ е к т а -к л а с с а или дочернего класса? -(BOOL) isMemberOfClass: объект-класс Является ли объект членом о б ъ е к т а - к л а с с а ? -(BOOL) respondsToSelector: селектор +(B00L) instancesRespondToSelector: селектор +(B00L)isSubclass0fClass: объект-класс Может ли объект отвечать на метод, указанный с е л е к т о р о м ? Могут ли экземпляры указанного класса отвечать на с е л е к т о р ? Является ли объект подклассом указанного класса? -(id) performSelector: selector -(id) performSelector: селектор withObject: объект -(id) performSelector: селектор withObject: объект 1 withObject: объект2 Применение метода, указанного селектором. Применение метода, указанного селектором , с передачей аргумента о б ъ е к т . Применение метода, указанного селектором , с передачей аргументов объект1 и объект2.
Имеются и другие методы. Один из них позволяет спрашивать, согласуется ли объект с определенным протоколом (см. главу 11). Другие позволяют спрашивать о динамически разрешаемых методах (в этой книге они не рассматриваются).
Чтобы создать объект-класс из имени класса или другого объекта, нужно передать ему сообщение class. Например, чтобы получить объект-класс из класса с именем Square, напишите строку [Square class]
Если mySquare является экземпляром объекта типа Square, то для получения его объекта-класса нужно написать [mySquare class]
Чтобы проверить, являются ли объекты, хранящиеся в переменных obj1 и obj2, экземплярами одного класса, нужно написать строку if ([obj1 class] == [obj2 class])
Чтобы убедиться, что объект myFract является членом класса Fraction, нужно проверить результат выражения [myFract isMemberOfClass: [Fraction class]]
Чтобы генерировать один из так называемых селекторов, которые приводятся в таблице 9.1, нужно применить директиву @selector к имени метода. В следующей строке создается значение типа SEL для метода с именем alloc, который является наследуемым методом из класса NSObject. (@selector (alloc)
В следующем выражении создается селектор для метода setTo:over:, который мы реализовали в классе Fraction (вспомните о двоеточияхв именах методов). (@selector (setTo:over:)
Чтобы убедиться, что экземпляр класса Fraction отвечает на метод setTo:over:, можно проверить возвращаемое значение из следующего выражения. [Fraction instancesRespondToSelector: (@selector (setTo:over:)]
Эта проверка охватывает и наследуемые, и непосредственно определенные в определении класса методы.
Метод performSelector: и его вариации (не показанные в таблице 9.1) позволяют передавать объекту сообщение, где сообщение может быть селектором, хранящимся внутри переменной. Рассмотрим следующую последовательность кода. SEL action; id graphicObject; action = @selector (draw); [graphicObject performSelector: action];
Метод, указанный переменной action типа SEL, передается графическому объекту, который хранится в graphicObject. Предполагается, что действие (action) может изменяться во время выполнения программы в зависимости от ввода пользователя, несмотря на указанное действие draw. Чтобы убедиться, что объект может ответить на данное действие, нужно использовать, например, следующую последовательность. if ([graphicObject respondsToSelector: action] == YES) [graphicObject perform: action] else // здесь должен быть код для обработки ошибок