Программирование на Objective-C 2.0
Шрифт:
В программе 11.1 секции interface и implementation для новой категории помещены в один файл вместе с тестовой программой. Как уже говорилось выше, секция interface для категории может быть включена в исходный header-файл Fraction.h (чтобы все методы были объявлены в одном месте), или в свой соб-ственный header-файл.
Если поместить категорию в мастер-файл определения класса, все пользователи этого класса будут иметь доступ к методам данной категории. Если у вас пет возможности непосредственного внесения изменений в исходный header- файл (см. добавление категории в существующий класс из библиотеки в части II, «Foundation Framework»), то вы вынуждены хранить категорию в отдельном файле. Некоторые
Отметим несколько особенностей категорий. Во-первых, хотя категория имеет доступ к переменным экземпляра исходного класса, в ней нельзя добавить ее собственные переменные экземпляра. При необходимости нужно использовать подклассы.
Кроме того, ка тегория может замещать другой метод своего класса. Обычно это считается недопустимым в практике надежного программирования. После замещения метода вы уже нс имеете доступа к исходному методу, поэтому при замене вы должны аккуратно дублировать все функциональные возможности замещаемого метода. Если вам действительно требуется замещение какого-либо метода, используйте подклассы. При замещении метода в подклассе вы можете по-прежнему обращаться к родительскому методу, передавая сообщение super. В этом случае вам не нужно знать все особенности метода, который вы замешаете; можно просто вызвать родительский метод и добавить ваши функциональ-ные возможности в метод подкласса.
Соблюдая изложенные здесь правила, можно иметь сколько угодно категорий. Если метод определен более чем в одной категории, язык не указывает, какая из них будет использоваться.
В отличие от обычной секции interface, вам не нужно реализовать все методы, указанные в категории. Это полезно для пошаговой разработки программ, поскольку вы можете объявить все методы в категории и постепенно их реализовывать.
Расширение класса путем добавления новых методов с помощью категории влияет не только на этот класс, по и на все его подклассы. Это может быть потенциально опасным. Например, если вы добавляете новые методы в корневой объект NSObject, каждый пользователь будет наследовать эти методы. Новые методы, которые вы добавляете к существующему классу с помощью категории, возможно, будут отвечать вашим намерениям, но могут оказаться несогласованными с исходной организацией или целями класса. Например, превращение квадрата (Square) в окружность (Circle) путем добавления новой категории и некоторых методов искажает определение класса и не согласуется с практикой надежного программирования.
Кроме того, пары объект/категория должны быть уникальными. Только одна категория NSString (Private) может существовать в заданном пространстве имен Objective-C. Это может вызывать затруднения, поскольку пространство имен Objective-C совместно используется кодом программы и всеми библиотеками, структурами framework (фреймворками) и дополнительными программными модулями (plug-in). Это особенно важно для программистов Objective-C, которые пишут коды экранных заставок (screensaver), панелей предпочтений и других дополнительных модулей, поскольку их код будет вставляться в код приложения или фреймворк, которыми они не могут управлять. 11.2. Протоколы
Протокол (protocol) — это список методов, которые совместно используются классами. Методы, включенные в протокол, не имеют соответствующих реализаций (implementation): предполагается, что они будут реализованы кем-то другим. Протокол — это способ определения набора методов, которые каким-либо образом связаны с указанным именем. Эти методы обычно документируются, что позволяет вам реализовать их в ваших определениях классов.
Если вы реализуете все необходимые методы для определенного протокола, вы подчиняетесь (conform) этому протоколу или принимаете (adopt) его.
Протокол определяется
просто: укажите имя протокола после директивы @protocol. После этого нужно объявить методы так же, как в секции interface. Все объявления методов вплоть до директивы @end становятся частью данного протокола.При работе с Foundation framework вы увидите, что несколько протоколов уже определены. Один из них, NSCopying, объявляет метод, который вам потребуется реализовать, если ваш класс должен поддерживать копирование объектов с помощью метода сору (или copyWrthZone:). (Подробно тема копирования объектов рассматривается в главе 18.)
Ниже показано, как определяется протокол NSCopying в стандартном файле Foundation NSObject.h. @protocol NSCopying - (id)copyWithZone: (NSZone *}zone; @end
Если вы приняли протокол NSCopying в своем классе, то должны реализовать метод copyWithZone:. Вы сообщаете компилятору, что принимаете протокол, заключая имя этого протокола в угловые скобки (<...>) в строке @interface. Имя про школа указывается после имени класса и его родительского класса, как в следующей строке: @interlace AddressBook: NSObject <NSCopying>
В этой строке сообщается, что AddressBook является объектом с родительским классом NSObject и подчиняется протоколу NSCopying. Поскольку система уже знает о методах, определенных ранее для этого протокола (в данном случае — из файла NSObject.h), эти методы не нужно объявлять в секции interface. Однако их нужно определить в вашей секции implementation.
В данном примере в секции implementation для AddressBook компилятор предполагает обнаружить определение метода copyWithZone:.
Если ваш класс принимает более одного протокола, просто перечислите их в угловых скобках, разделяя запятыми: @interface AddressBook: NSObject <NSCopying, NSCoding>
Здесь компилятору сообщается, что класс AddressBook принимает протоколы NSCopying и NSCoding. В данном случае компилятор предполагает обнаружить определение всех требуемых методов (перечисленных в этих протоколах) в секции implementation для AddressBook.
Определив свой собственный протокол, вы не обязаны реализовать его сам и. Вы уведомляете других программистов, что если они хотят принять этот протокол, то должны реализовать соответствующие методы. Эти методы могут наследоваться из суперкласса. Так, если класс подчиняется протоколу 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.