黑马程序员_OC中的分类(category)

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------


在编写面向对象的程序的时候,我们经常想为现有的类添加一些新的行为。例如,你设计了一种新的类型轮胎,因此需要创建Tire类的子类并添加一些有趣的功能。为已经存在的类添加行为时,通常采用创建子类的方法。但是,有时创建子类并不方便。比如,你想要为NSString类添加一个新的方法。但是NSString实际上只是一个类簇的表面形式,因而这样的类创建子类非常困难。利用Objective-C的动态运行分配机制,尼克为现有的类添加新的方法。这些新方法在OC中被称为分类(category)。

创建分类

例如,你想要编写一个程序接收一系列的字符串,然后确定每个字符串的长度并存入NSArray或NSDictionary中。你需要先将每个长度的值包装在一个NSNumber对象中,然后才能存入NSArray或NSDictionary中。如:

NSNumber *number;
number = [NSNumber numberWithUnsignedInt: [string length]];
// … do something with number

我们可以使用分类来简化。通常把分类放在独立的文件中,以“类名称+分类名称”命名。这个程序可以写成如下:

@interface 部分

@interface NSString (NumberConvenience)
- (NSNumber *)lengthAsNumber;

@end

@implementation 部分

#import "NSString+NumberConvenience.h"

@implementation NSString (NumberConvenience)

- (NSNumber *)lengthAsNumber
{
    NSUInteger length = [self length];
    return ([NSNumber numberWithUnsignedInt:length]);
}

@end

然后是main函数:

#import <Foundation/Foundation.h>
#import "NSString+NumberConvenience.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        NSMutableDictionary *dic = [NSMutableDictionary dictionary];
        
        [dic setObject:[@"hello" lengthAsNumber] forKey:@"hello"];
        [dic setObject:[@"iLikeFish" lengthAsNumber] forKey:@"iLikeFish"];
        [dic setObject:[@"Once upon a time" lengthAsNumber] forKey:@"Once upon a time"];
        
        NSLog(@"%@", dic);

    }
    return 0;
}

程序运行后,得到如下结果:

分类的缺陷

分类有相隔局限性。

  • 无法向类中添加新的实例变量。分类没有空间容纳实例变量
  • 名称冲突,也就是分类中的方法与现有的方法重名。当发生名称冲突时,分类具有更高的优先级导致现有的方法不可以用。

分类的优势

  • 将类的实现代码分散到多个不同文件或框架中
  • 创建对私有方法的向前引用
  • 向对象添加非正式协议


类扩展

有一个特殊的分类叫类扩展(class extension)。这个类的特点之一就是不需要名字。其特点如下:

  • 它不需要名字
  • 你可以在包含你的源代码的类中使用它
  • 你可以添加变量
  • 可以将只读权限改成读写权限
  • 创建数量不限

下面是一个示例:

things.h如下:

#import <Foundation/Foundation.h>

@interface Things : NSObject
@property (assign) NSInteger thing1;
@property (readonly, assign) NSInteger thing2;

- (void)resetAllValues;
@end

这个类包含两个属性和一个方法。将thing2属性标记为只读。

thing.m如下:

#import "Things.h"

@interface Things ()
{
    NSInteger thing4;
}
@property (readwrite, assign) NSInteger thing2;
@property (assign) NSInteger thing3;
@end

@implementation Things
@synthesize thing1;
@synthesize thing2;
@synthesize thing3;

- (void)resetAllValues
{
    self.thing1 = 200;
    self.thing2 = 300;
    self.thing3 = 400;
    thing4 = 5;
}

- (NSString *) description
{
    NSString *desc = [NSString stringWithFormat: @"%d %d %d",
                      thing1, thing2, thing3];
    
    return (desc);
    
} // description
@end

我们看其中的代码:

@interface Things ()
{
    NSInteger thing4;
}
@property (readwrite, assign) NSInteger thing2;
@property (assign) NSInteger thing3;
@end

这里修改了thing2的属性,将只读变成了读写。我们改变了读写权限,将它标记为readwrite,这样编译器就会生成setter方法,不过它是只能在这个类中访问的私有方法,在公共接口只有getter方法。而且添加了thing3属性。此外,还添加了一个名为thing4的实例变量。

main.m文件如下:

#import <Foundation/Foundation.h>
#import "Things.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool
    {
        Things *things = [[Things alloc] init];
        things.thing1 = 1;
        NSLog(@"%@", things);
        [things resetAllValues];
        NSLog(@"%@", things);
    }
    return 0;
}

运行程序,会得到如下结果:

1 0  0

200 300 400

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值