Категории в Objective-C - это механизм позволяющий расширять существующие классы. Используя категории к классу можно добавить новые методы без применения наследованная и не имея исходников изменяемого класса. Добавляемые таким способом методы становятся доступными всем классам, унаследованным от изменяемого. С помощью категорий также можно переопределять существующие методы. Категория имеет доступ к переменным экземпляра исходно класса, однако с помощью категорий невозможно добавить новые переменные экземпляра.
Объявление категории похоже на объявление класса:
@interface NSString (MyCategory)
-(void) myMethod;
@end
Описание категории начинается с директивы @interface
, после которой следует имя расширяемого класса, в примере это NSString
. Затем в круглых скобках (...)
указывается название категории. После этого описываются добавляемые в класс методы. Описание категории заканчивается директивой @end
К классу можно добавить любое кол-во категорий, но они должны иметь уникальные имена.
Реализация категории также похожа на реализацию класса и не вызывает сложностей:
@implementation NSString (MyProperty)
-(void) myMethod {
// тело метода
}
@end
Реализация категории начинается с директивы @implementation
, после которой следует имя расширяемого класса и название категории, заданное в круглых скобках. После этого идет реализация добавляемых методов. Реализация категории заканчивается директивой @end
Категории можно использовать для того, чтобы обеспечить реализацию классом каких-либо протоколов.
@protocol MyProtocol
-(void) myMethod;
@end
@interface NSString (MyCategory) <MyProtocol>
@end
@implementation NSString (MyCategory)
-(void) myMethod {
// Реализация метода
}
@end
Здесь мы говорим, что класс NSString
принимает протокол MyProtocol
и реализует его обязательный метод myMethod
Категории можно использовать для раннего объявления методов, а также для раскрытия приватных методов. Допустим у вас имеется класс MyTestClass
с приватным методом privateMetod
:
// Описание класса, h - файл
@interface MyTestClass: NSObject
-(void) publicMethod;
@end
// Реализация класса, m - файл
@interface MyTestClass()
-(void) _privateMethod;
@end
@implementation MyTestClass
-(void)publicMethod {
...
}
-(void) _privateMethod {
...
}
@end
Если из другого класса вы обратитесь к методу privateMetod
, то получите сообщение об ошибке во время компиляции:
No visible @interface for 'MyTestClass' declares the selector 'privateMethod'
Для предотвращения этого можно воспользоваться категорией. В файле реализации класса, в котором вы обращаетесь к приватному методу класса MyTestClass
, опишите категорию:
@interface MyTestClass (PrivateMetod)
-(void)_privateMetod;
@end
и вы больше не получите сообщение об ошибке во время компиляции. При этом мы только описали категорию, но не реализовали ее.
Неформальные протоколы
Методы описанные в категории необязательны для реализации. Категория с описанными, но не реализованными методами называется неформальным протоколом. Неформальные протоколы часто определяются для корневого класса NSObject
, при этом они не имею реализации в самом классе. Каждый подкласс будет отвечать на сообщения описанные в категории, но при этом будут делать что-то полезное только реализованные методы. Методы, по мере необходимости, могут быть реализованы в подклассах, при этом реализуемые методы должны быть объявлены в секции @interface
.