Программирование на Objective-C 2.0
Шрифт:
В этой строке сообщается, что AddressBook является объектом с родительским классом NSObject и подчиняется протоколу NSCopying. Поскольку система уже знает о методах, определенных ранее для этого протокола (в данном случае — из файла NSObject.h), эти методы не нужно объявлять в секции interface. Однако их нужно определить в вашей секции implementation.
В данном примере в секции implementation для AddressBook компилятор предполагает обнаружить определение метода copyWithZone:.
Если ваш класс принимает более одного протокола, просто перечислите их в угловых скобках, разделяя запятыми: @interface AddressBook: NSObject <NSCopying, NSCoding>
Здесь
Определив свой собственный протокол, вы не обязаны реализовать его сам и. Вы уведомляете других программистов, что если они хотят принять этот протокол, то должны реализовать соответствующие методы. Эти методы могут наследоваться из суперкласса. Так, если класс подчиняется протоколу NSCopying, это действительно и для его подклассов (хотя и не означает, что эти методы правильно реализованы для данного подкласса).
Протокол позволяет определить методы, которые будут реализовать другие люди, использующие подкласс нашего класса. Например, вы можете определить протокол Drawing для своего класса GraphicObjcct; п нем можно определить методы paint (окраска), erase (стирание) и outline (контур). @protocol Drawing -(void) paint; -(void) erase; @optional -(void) outline; @end
Создав класс GraphicObject, вы не обязаны реализовать эти методы, однако вы должны указать методы, которые должен реализовать человек, создающий подкласс класса GraphicObject, чтобы соответствовать стандарту для создаваемых объектов рисования.
Примечание. Любые методы, которые указаны после директивы @optional директивы, не являются обязательными. Что человек, принявший протокол Drawing, не обязан реализовать метод outline, подчиняясь этому протоколу. (Вы можете вернуться к перечислению обязательных протоколов с помощью директивы @required в определении протокола.)
Таким образом, если вы создаете подкласс Rectangle класса GraphicObject и объявляете (то есть документируете), что ваш класс Rectangle подчиняется протоколу Drawing, пользователи данного класса будут знать, что они могут передавать экземплярам этого класса сообщения paint, erase и (возможно) outline.
Примечание. Это теория. Компилятор позволяет вам указать, что вы подчиняетесь протоколу, и выдает предупреждающие сообщения, только если вы не реализуете эти методы.
Отметим, что в протоколе нет ссылки ни на какие классы; это «бесклассовое» средство. Протоколу Drawing может подчиняться любой класс, не только подклассы GraphicObject.
Чтобы проверить, подчиняется ли объект какому-либо протоколу, можно использовать метод conformsToProtocol:. Например, вы хотите определить, подчиняется ли объект с именем currentObject протоколу Drawing, чтобы передавать этому объекту сообщения для рисования. Для этого можно написать следующее. id currentObject; ... if ([currentObject conformsToProtocol: @protocol (Drawing)] == YES) { // Передача сообщений currentObject paint, erase и/или outline ... }
Специальная директива @protocol, которая используется здесь, принимает имя протокола и создает объект типа Protocol, который используется как аргумент методом conformsToProtocol:.
Вы можете воспользоваться помощью компилятора,
Это указывает компилятору, что cunentObject будет содержать объекты, подчиняющиеся протоколу Drawing. Если присвоить currentObject объект статического типа, который не согласуется с протоколом Drawing (например, у вас есть несогласуюш ийся класс Square), то компилятор выдаст предупреждающее сообщение: warning: class 'Square' does not implement the 'Drawing' protocol (предупреждение: класс 'Square' не реализует протокол 'Drawing')
Здесь проверку выполняет компилятор, поэтому присваивание currentObject переменной типа id не приведет к выводу этого сообщения. Ддля объекта, хранящегося в переменной типа id, компилятор не сможет определить, подчиняется ли он протоколу Drawing.
В списке можно указать более одного протокола, если переменная будет содержать объект, подчиняющийся нескольким протоколам: id <NSCopying, NSCoding> myDocument;
Определение протокола можно расширить. В следующем объявлении протокола указывается, что протокол Drawing3D принимает также протокол Drawing. @protocol Drawing3D <Drawing>
Таким образом, класс, который принимает протокол Drawing3D, должен реализовать методы, перечисленные для этого протокола, а также методы из протокола Drawing.
И, наконец, категория тоже может принимать протокол, например: @interface Fraction (Stuff) <NSCopying, NSCoding>
Здесь Fraction содержит категорию Stuff, которая принимает протоколы NSCopying и NSCoding.
Как и имена классов, имена протоколов должны быть уникальными. Неформальные протоколы
В литературе встречается понятие неформального, свободного (informal) протокола. На самом деле это категория, содержащая список группы методов, но не реализующая их. Все (или почти все) наследуется из одного корневого объекта, поэтому неформальные категории часто определяются для корневого класса. Иногда неформальные протоколы называют также абстрактными (abstract) протоколами.
В header-файле могут встретиться объявления методов, которые выглядят следующим образом: @interface NSObject (NSComparisonMethods) -(BOOL)isEqualTo:(id)object; -(BOOL)isLessThanOrEqualTo:(id)object; -{B00L}isLessThan:(id)object; -<BOOL}isGreaterThanOrEqualTo:(id)object; BOOLJisGreaterThan: (id)object; -(BOOL)isNotEqualTo:(id)object; -{BOOL)doesContain:(id)object; -(B00L)isLike:(NSString *)object; -(BOOL)isCaselnsensitiveLike:(NSString *)object; @end
Здесь определяется категория с именем NSComparisonMethods для класса NSObject. В этом неформальном протоколе содержится список группы методов (в данном случае — девять), которые могут быть реализованы как часть этого протокола. Неформальный протокол — это фактически просто группа методов под определенным именем. Это полезно с точки зрения документирования и модульной организации методов.
Класс, где объявляется неформальный протокол, не реализует методы в самом этом классе. В подклассе, выбранном для реализации этих методов, требуется переобъявить их в секции interface, а также реализовать один или несколько из этих методов. В отличие от формальных протоколов, компилятор не оказывает никакой помощи с неформальными протоколами: здесь нет никакой концепции подчинения или проверки компилятором.