类别(Category)是OjectiveC的一个特性,主要目的是让开发者可以以模块的形式向类添加方法(扩展),创建标准化的方法列表供给其他人实现。
有些文档也会翻译成类别,其实是一个意思。
概述
语法说明
类别提供了一个简单的方式,将类定义模块化到相关组或分类中,同时也提供了扩展现有类定义的简便方式。换句话说,它的功能有些类似于接口继承,但又比继承简单和强大,在使用时需要结合继承一起使用,其能力是非常厉害的。 分类的语法如下:
objectivec
@interface Fraction(Category1) //类名+(类别名称)
//分类方法的代码
@end
@interface Fraction(Category2)
//分类方法的代码
@end
类别的作用
如果在xcode中使用新建文件功能创建Cocoa class文件,会自动生成.h和.m文件。在这种分离的设计基础上附加分类后,可以使用分类实现下面三个方面的能力增强:
- 扩展现有类库的功能:比如扩展NSString、NSObject类,这种方式某些书中扩展NSObject类有时被称为非正式协议;
- 扩展现有类的声明:定义一个声明,然后分模块实现,比如在一个.h文件中定义类声明,然后分多个.m实现,一个.m实现其中一个分类,这样方便分工合作;
- 为现在的自定义类添加私有方法而不必重新定义子类;
类别规则
- 类别文件名称一般是是"类名称+类别名称"来命名,这是一个不成文的规定;
- 类别中可以添加属性,但不能添加实例变量,而且属性必须是@dynamic类型的;
- 如果类别方法与现有类的方法重名,类的方法会被覆盖掉,一旦覆写后外部就不能访问原来的方法了,所以这块要特殊设计一下;
- 如果多个分类中有同名的方法,可能会引起混乱;分类后不仅会影响当前类,也会影响所有的子类;
- 类别的名称是在类的范围内是唯一的;
创建类别
在新建文件中,选择Objective-C文件类型
在配置界面做如下配置,注意File Type选择 Category:
最后生成如下项目工程文件如下:
类别能力
给现有类添加新方法
类别可以为所有类添加新方法,包括系统类库和自定义的类。下面的代码就用一个自定义的类别来扩展系统提供的NSString类库的功能。
- 类别定义
objectivec
//扩展系统NSString,添加一个名为NumberConvenience的类别
#import <Foundation/Foundation.h>
@interface NSString (NumberConvenience)
- (NSNumber *)lengthAsNumber; //添加一个新方法
@end
- 类别实现
objectivec
#import "NSString+NumberConvenience.h"
@implementation NSString (NumberConvenience)
- (NSNumber *)lengthAsNumber{
NSUInteger length = [self length];
return ([NSNumber numberWithUnsignedInt:length]);
} // lengthAsNumber
@end
- 类别测试
objectivec
#import <Foundation/Foundation.h>
#import "NSString+NumberConvenience.h"
int main(int argc, const char * argv[]){
@autoreleasepool
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:[@"hello" lengthAsNumber] forKey:@"hello"];
NSLog (@"%@", dict);
}
return 0;
}
类能力(声明)的动态扩展
有别于上面的给现有类添加新方法的实现,类功能的动态扩展不需要新建文件,也可以实现定义实例变量。
方法是创建一个未命名的分类,然后在()中不指定任何名字,在这个类中可以扩展实例变量和属性(在有命名的分类中是不允许的)。未命名分类中的方法需要在主实现区中实现,而不是在分离的实现区中实现。
在未命名类别中的扩展的属性必须是私有的
- 自定义类,Fraction是一个自定义的类,用于此示例的测试
objectivec
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
//原始接口声明
@interface Fraction : NSObject
@property (assign) NSInteger thing1;
//标记为只读属性
@property (readonly, assign) NSInteger thing2;
- (void)resetAllValues;
@end
- 扩展自定义类的属性和方法
objectivec
@interface Fraction() //空
NS_ASSUME_NONNULL_END
{
NSInteger thing4; //添加了新实例变量
}
@property (readwrite, assign) NSInteger thing2; //覆盖了原有属性
@property (assign) NSInteger thing3; //添加了新私有属性
@end
- 实现自定义类
objectivec
#import "Fraction.h"
@implementation Fraction
- (void)resetAllValues
{
self.thing1 = 200;
self.thing2 = 300;
self.thing3 = 400;
thing4 = 5;
}
- (NSString *) description
{
NSString *desc = [NSString stringWithFormat: @"%d %d %d %d", _thing1, _thing2, _thing3, thing4];
return (desc);
} // description
@end
- 测试代码
objectivec
#import <Foundation/Foundation.h>
#import "NSString+NumberConvenience.h"
#import "Fraction.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:[@"hello" lengthAsNumber] forKey:@"hello"];
NSLog (@"%@", dict);
Fraction *things = [[Fraction alloc] init];
things.thing1 = 1;
NSLog(@"%@", things); //~1 0 0 0
//重新赋值
[things resetAllValues];
NSLog(@"%@", things); //~200 300 400 5
}
return 0;
}
类分文件实现
前面所有的代码实现全是一个.h,一个m。如果是大型项目的话.m中的代码量可能非常区大,这里可以采用两种不同的设计方案解决:拆类或是分类。除了代码整洁外,还可以多人协作以及实现代码分包存储等功能,打破了继承这种约束。
即定义一个.h,多个.m实现这样的模式来分散类的实现,每个.m实现一个类别接口,示例代码如下:
类别定义
objectivec
//类别定义
#import <Foundation/Foundation.h>
@interface CategoryThing : NSObject
{
NSInteger thing1;
NSInteger thing2;
NSInteger thing3;
}
@end // CategoryThing
// Category thing1
@interface CategoryThing (Thing1)
- (void)setThing1:(NSInteger)thing1;
- (NSInteger)thing1;
@end
// Category thing2
@interface CategoryThing(Thing2)
- (void)setThing2:(NSInteger)thing2;
- (NSInteger)thing2;
@end // CategoryThing (Thing2)
// Category thing3
@interface CategoryThing (Thing3)
- (void)setThing3:(NSInteger)thing3;
- (NSInteger)thing3;
@end // CategoryThing (Thing3)
类别实现
objectivec
#import "CategoryThing.h"
@implementation CategoryThing (Thing1)
- (void)setThing1:(NSInteger)t1{
thing1 = t1;
}
- (NSInteger) thing1{
return (thing1);
}
@end // CategoryThing
objectivec
#import "CategoryThing.h"
@implementation CategoryThing (Thing2)
- (void)setThing2:(NSInteger) t2{
thing2 = t2;
}
- (NSInteger)thing2{
return (thing2);
}
@end
objectivec
\#import "CategoryThing.h"
@implementation CategoryThing (Thing3)
- (void)setThing3:(NSInteger) t3{
thing3 = t3;
}
- (NSInteger)thing3{
return (thing3);
}
@end
程序测试
main测试
objectivec
#import <Foundation/Foundation.h>
#import "CategoryThing.h"
int main(int argc, const char * argv[])
{
@autoreleasepool
{
CategoryThing *thing = [[CategoryThing alloc] init];
[thing setThing1: 5];
[thing setThing2: 23];
[thing setThing3: 42];
NSLog (@"Things are %@", thing); //Things are 5 23 42
}
return 0;
}
end...