类别(Category)是OjectiveC的一个特性,主要目的是让开发者可以以模块的形式向类添加方法(扩展),创建标准化的方法列表供给其他人实现。
有些文档也会翻译成类别,其实是一个意思。
概述
语法说明
类别提供了一个简单的方式,将类定义模块化到相关组或分类中,同时也提供了扩展现有类定义的简便方式。换句话说,它的功能有些类似于接口继承,但又比继承简单和强大,在使用时需要结合继承一起使用,其能力是非常厉害的。 分类的语法如下:
@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类库的功能。
- 类别定义
//扩展系统NSString,添加一个名为NumberConvenience的类别
#import <Foundation/Foundation.h>@interface NSString (NumberConvenience)- (NSNumber *)lengthAsNumber; //添加一个新方法@end
- 类别实现
#import "NSString+NumberConvenience.h"@implementation NSString (NumberConvenience)- (NSNumber *)lengthAsNumber{NSUInteger length = [self length];return ([NSNumber numberWithUnsignedInt:length]);
} // lengthAsNumber@end
- 类别测试
#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是一个自定义的类,用于此示例的测试
#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN//原始接口声明
@interface Fraction : NSObject@property (assign) NSInteger thing1;
//标记为只读属性
@property (readonly, assign) NSInteger thing2;- (void)resetAllValues;@end
- 扩展自定义类的属性和方法
@interface Fraction() //空
NS_ASSUME_NONNULL_END
{NSInteger thing4; //添加了新实例变量
}
@property (readwrite, assign) NSInteger thing2; //覆盖了原有属性
@property (assign) NSInteger thing3; //添加了新私有属性
@end
- 实现自定义类
#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
- 测试代码
#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实现一个类别接口,示例代码如下:
类别定义
//类别定义
#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)
类别实现
#import "CategoryThing.h"@implementation CategoryThing (Thing1)
- (void)setThing1:(NSInteger)t1{thing1 = t1;
}- (NSInteger) thing1{return (thing1);
}@end // CategoryThing
#import "CategoryThing.h"@implementation CategoryThing (Thing2)- (void)setThing2:(NSInteger) t2{thing2 = t2;
}- (NSInteger)thing2{return (thing2);
}@end
\#import "CategoryThing.h"@implementation CategoryThing (Thing3)- (void)setThing3:(NSInteger) t3{thing3 = t3;
}- (NSInteger)thing3{return (thing3);
}@end
程序测试
main测试
#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…