您的位置:首页 > 移动开发 > Objective-C

从C/C++到Objective-C(五)--- 类别的使用

2016-03-12 16:28 316 查看
C++中的很多类我们是不能继承它们的,比如stl的string类,如果使用该类,那我们也就只能使用该类所提供的方法。在OC中也有类似的问题,当我们想为某个类,如NSString添加一个新的行为时,一般的方法是直接创建该类的子类,然后再在子类中实现对应的方法,但是NSString类实际上市一个类簇的表面形式,所以要创建这种类的子类是很累人的一件事情。在其他情况下,就算是你可以创建它的子类,但是当你用到对应的工作集和类库是无法帮你创建新对象的。

所以在OC中利用OC的动态运行时分配机制,我们可以为现有的类添加新的方法,这些方法在OC中就称为类别。

例如,当我们正在写某一个程序时,该程序会要求接收一系列的字符串,然后确定每个字符串的长度,并将该长度存到一个数组或者是和对应的字符串存到字典当中。而且我们要在求得某个字符串的长度后,再将其包装在一个NSNumber中才能将其存入NSArray或者是NSDictionary中。所以代码类似下面这样:

NSNumber *number;
number = [NSNumber numberWithUnsignedInt: [string length]];
//...do something如果在很多地方都要写这样一段代码那显然是很繁琐的,或者你会想到将其写成一个方法放到使用的类中去,这样直接调用该类的方法就ok了,但是这样写了后你这个方法还不是只能在该类中使用,所以也不是很理想的,理想的做法是直接为NSString添加一个类别,只要将该类别的头文件放到你要使用的文件中就可以像使用NSString类方法那样使用类别中的方法了。首先我们看下类别的声明:
@interface NSString (NumberConvenience)
- (NSNumber *)lengthAsNumber;
@end可以看到类别的声明和类的声明是很相似的,也就多了一个在类名称后面的一个括号。括号内的内容NumberConvenience也就是类别的名称,也就是说我们为NSString类添加了一个名为NumberConvenience的类别。只要保证类别的名称唯一,我们就可以向一个类中添加任意数量的类别。你可以向类别中添加属性但是不能添加实例变量,而且属性必须是@dynamic类型的。添加属性的好处是你可以通过点表达式访问setter和getter方法。下面是类的实现代码:
#import "NSString+NumberConvenience.h" //这里的命名方式一般采用类+类别名称的方式

@implementation NSString (NumberConvenience)
- (NSNumber *)lengthAsNumber
{
NSUInteger length = [self length];

return ([NSNumber numberWithUnsignedInt:length]);

} // lengthAsNumber
@end这里的实现代码中也包含了类别的名称。这样实现了后我们就可以直接对NSString字符串调用lengthAsNumber方法了。在创建类别的时候也要注意内存管理,在这里我们采用的是numberWithUnsignedInt不属于alloc copy new方法,因此它返回的对象是保留计数器为1并且已经被设置成了自动释放了,所以内存管理是安全的。使用如下:
#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"];

[dict setObject:[@"iLikeFish" lengthAsNumber]
forKey:@"iLikeFish"];

[dict setObject:[@"Once upon a time" lengthAsNumber]
forKey:@"Once upon a time"];

NSLog (@"%@", dict);
}
return 0;
}注意,我们创建了NSString的类别后,任何NSString对象都能相应lengthAsNumber消息,包括字面量字符串、description方法返回的字符串、可变字符串、文件中加载的字符串等等。正是这种兼容性是类别成为了一种非常强大的概念。通过它不需要创建NSString类的子类就可以获得一种新的行为。
当然类别也有一些局限性。首先是无法向类别中添加新的实例变量。类别没有空间容纳实例变量。其次是名称的冲突,我们在类别中命名的方法名可能和类现有的方法名重名,当发生命名冲突时,类别的优先级更高,所以会先相应类别中的方法。所以保险的做法是为类别中的方法名加一个前缀,以避免这个问题。

类别还可以用来分散类的实现代码。这个怎么理解呢?比如说,在我们编写一个用户操作类的时候,可能会在该类中包含很多的方法声明 ,其实现文件中也包含了大量的实现代码。这样的问题在系统类中就明显了,所以通过利用类别,我们可以把这些方法的实现分开,放在不同的实现源文件中。例如:

@interface CategoryThing : NSObject
{
NSInteger thing1;
NSInteger thing2;
NSInteger thing3;
}
@end // CategoryThing

@interface CategoryThing (Thing1) //声明了一个名为Thing1的类别
- (void)setThing1:(NSInteger)thing1;
- (NSInteger)thing1;
@end // CategoryThing (Thing1)

@interface CategoryThing(Thing2) //声明了一个名为Thing2的类别
- (void)setThing2:(NSInteger)thing2;
- (NSInteger)thing2;
@end // CategoryThing (Thing2)

@interface CategoryThing (Thing3) //声明了一个名为Thing3的类别
- (void)setThing3:(NSInteger)thing3;
- (NSInteger)thing3;
@end // CategoryThing (Thing3)在这个头文件中我们包含了一个CategoryThing类的接口声明和三个CategoryThing的类别声明。这个三个类别中分别对类CategoryThing中的不同变量进行了操作。在实现的时候我们就可以把三个类别放在不同的源文件中实现其对应的操作。如:
#import "CategoryThing.h" //注意包含头文件

@implementation CategoryThing (Thing1)
- (void)setThing1:(NSInteger)t1
{
thing1 = t1;
} // setThing1

- (NSInteger) thing1
{
return (thing1);
} // thing1

@end // CategoryThing我们可以把这个类别的实现放在一个单独的实现文件中,比如可以命名为thing1.m,其他两个类别的实现和操作也类似。这样就可以把某个类中的操作给分散开了,也便于我们后续的查看,当然当使用该类的时候只需包含类的头文件就行了。在使用一个对象时,对象的方法是在接口中声明、在父类中声明还是在类别中声明的并不重要,也不会影响使用的结果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: