Категория — это языковое средство 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 мог реагировать на них в любом месте кода моего приложения.
Прикладной код был структурирован следующим образом (он приводится здесь лишь для одного метода):
Если бы метод basePictureName был реализован как служебный метод в каком-ни-будь другом классе, ему нужно было бы принимать параметр, в качестве которого пришлось бы передавать объект типа NSString. Если бы это был метод экземпляра, то пришлось бы дополнительно получать ссылку на экземпляр данного класса. В этом отношении категория более аккуратна и компактна. Достаточно было расширить сам класс NSString, чтобы получить basePictureName в качестве метода его экземпляра. Таким образом, из любого файла с расширением .т, импортирующего файл StringCategories .h, можно отправить сообщение типа basePictureName непосредственно любому объекту типа NSString, который требуется преобразовать, как показано ниже.
NSString* aName - [someString basePictureName];
Категория особенно пригодна для таких классов, как NSString, поскольку в документации предупреждается, что наследование этого класса выполнять нецелесообразно. Дело в том, что класс NSString является составной частью более сложных классов, называемых кластером классов, а это означает, что настоящим классом объекта типа NSString на самом деле может оказаться совсем другой класс. Категория оказывается намного более удобной для видоизменения класса в кластере классов, чем наследование.
Метод, определяемый с помощью категории, может быть в равной степени методом класса. Таким образом, служебные методы можно внедрить в любой подходящий класс и затем вызывать их без дополнительных издержек на получение каких бы то ни было экземпляров. Классы доступны глобально, а следовательно, их методы, по существу, становятся глобальными (см. главу 13).
Например, в одном из своих приложений я обнаружил часто используемый определенный цвет (UlColor). Вместо того чтобы повторять инструкции для формирования этого цвета всякий раз, когда он понадобится, я разместил эти инструкции в одном месте: в разделе реализации категории в файле с расширением . ш, как показано ниже.
Метод myGolden я объявил в разделе интерфейса категории соответствующего файла с расширением . h и организовал импорт этого файла в файле своего проекта с расширением . pch (предварительно скомпилированном файле заголовка). Поскольку предварительно скомпилированный файл заголовка автоматически импортируется через мой проект, то я могу теперь делать вызов [UlColor myGolden] откуда угодно.