Категория — это языковое средство Objective-C, позволяющее перейти непосредственно к существующему классу и внедрить в него дополнительные методы.

Это можно сделать даже в отсутствие кода для класса, как, например, для классов Cocoa. Методы экземпляра могут обращаться по ссылке self, а это, как обычно, будет означать экземпляр, которому первоначально послано сообщение. В отличие от подкласса, в категории нельзя определить дополнительные переменные экземпляра. В ней можно заместить методы, но вряд ли стоит пользоваться такой возможностью. Категория определяется практически так же, как и класс (см. главу 4). Для этого потребуются разделы интерфейса и реализации, которые, как правило, распределяются по парам файлов с расширениями .h и .т. Нужно, однако, иметь в виду следующее.

  • В разделе интерфейса не следует объявлять суперкласс, поскольку он уже существует, а его суперкласс — уже объявлен. Разумеется, в разделе интерфейса должно бы доступно объявление исходного класса, для чего, как правило, импортирует заголовочный файл этого класса (или заголовочный файл каркаса, в котором он определен).
  • Имя категории следует ввести в круглых скобках в начале обоих разделов интерфейса и реализации, где задается имя класса.

Если вы пользуетесь, как обычно, парой файлов с расширениями . h и . т, импортируйте файл с расширением . h. Если из любого другого файла с расширением . m в вашем проекте требуется вызвать какой-нибудь из методов, внедренных в категорию, то эти методы должны быть объявлены открытыми в файле с расширением . h данной категории, тогда как в файле с расширением . m должен быть организован импорт файла с расширением . h из данной категории.

Для того чтобы установить пару файлов с расширениями . h и . ш, проще всего обратиться к среде Xcode, где это будет сделано автоматически. С этой целью выберите сначала команду меню Flle^New^File, а затем вариант Objective-C category среди прочих типов файлов среды Cocoa Touch для системы iOS в открывшемся диалоговом окне Choose a template. Вам будет предложено присвоить имя новой категории и классу, в котором она определяется.

С другой стороны, если методы, внедренные в данной категории, требуется вызывать только из одного класса, то вполне благоразумно разместить оба раздела интерфейса и реализации данной категории в заголовочном файле класса с расширением . h. В этом случае раздел интерфейса может быть пустым, а методы, определенные в разделе реализации данной категории, могут быть доступны для применения в файле с расширением . т.

Например, в одном из моих приложений я обнаружил, что целый ряд строковых преобразований выполняются с целью вывести путь к различным файлам ресурсов в пакете приложения по имени и назначению ресурса. В итоге оказалось, что для этой цели применяется полдесятка служебных методов. Поскольку все эти методы оперировали объектами класса NSString, то их целесообразно было реализовать в виде категории типа NSString. Таким образом, любой объект типа NSString мог реагировать на них в любом месте кода моего приложения.

Прикладной код был структурирован следующим образом (он приводится здесь лишь для одного метода):

// StringCategories.h:

♦import <Foundation/Foundation,h>

@interface NSString (MyStringCategories)

- (NSString*) basePictureName;

@end

// StringCategories.m:

♦import "StringCategories.h"

@implementation NSString (MyStringCategories)

- (NSString*) basePictureName {

return [self stringByAppendingString:@"IO"];)

Send

Если бы метод basePictureName был реализован как служебный метод в каком-ни-будь другом классе, ему нужно было бы принимать параметр, в качестве которого пришлось бы передавать объект типа NSString. Если бы это был метод экземпляра, то пришлось бы дополнительно получать ссылку на экземпляр данного класса. В этом отношении категория более аккуратна и компактна. Достаточно было расширить сам класс NSString, чтобы получить basePictureName в качестве метода его экземпляра. Таким образом, из любого файла с расширением .т, импортирующего файл StringCategories .h, можно отправить сообщение типа basePictureName непосредственно любому объекту типа NSString, который требуется преобразовать, как показано ниже.

 

NSString* aName - [someString basePictureName];

 

Категория особенно пригодна для таких классов, как NSString, поскольку в документации предупреждается, что наследование этого класса выполнять нецелесообразно. Дело в том, что класс NSString является составной частью более сложных классов, называемых кластером классов, а это означает, что настоящим классом объекта типа NSString на самом деле может оказаться совсем другой класс. Категория оказывается намного более удобной для видоизменения класса в кластере классов, чем наследование.

Метод, определяемый с помощью категории, может быть в равной степени методом класса. Таким образом, служебные методы можно внедрить в любой подходящий класс и затем вызывать их без дополнительных издержек на получение каких бы то ни было экземпляров. Классы доступны глобально, а следовательно, их методы, по существу, становятся глобальными (см. главу 13).

Например, в одном из своих приложений я обнаружил часто используемый определенный цвет (UlColor). Вместо того чтобы повторять инструкции для формирования этого цвета всякий раз, когда он понадобится, я разместил эти инструкции в одном месте: в разделе реализации категории в файле с расширением . ш, как показано ниже.

(^implementation UlColor (MyColors)

+ (UlColor*) myGolden {

return [self colorWithRed:1.000 green:0.894 blue:0.541 alpha:.900]; }

Send

Метод myGolden я объявил в разделе интерфейса категории соответствующего файла с расширением . h и организовал импорт этого файла в файле своего проекта с расширением . pch (предварительно скомпилированном файле заголовка). Поскольку предварительно скомпилированный файл заголовка автоматически импортируется через мой проект, то я могу теперь делать вызов [UlColor myGolden] откуда угодно.


 

 

 

Добавить комментарий