Шрифт:
Вывод программы 11.1 1/3 + 2/5 ---- 11/15 1/3 - 2/5 ---- -1/15 1/3 * 2/5 ---- 2/15 1/3 / 2/5 ---- 5/6
И снова напомним, что в Objective-C вполне допустимы такие операторы, как [[a div: b] print];
В этой строке выполняется непосредственный вывод результата деления а на Ь, что позволяет избежать промежуточного присваивания результата какой- либо переменной, как в программе 11.1.Нам нужно это промежуточное присваивание, чтобы получить результирующую дробь (Fraction) и освободить память, которую она занимает. В противном случае при каждом выполнении арифметической операции над дробью будет происходить утечка памяти.
В программе 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>