OC基础教程8-类别

前言

在写程序时,有时候你想为现有的类添加一些新的行为。通常采用创建子类的方法。不过有时子类并不方便。比如说,你想要为NSString类添加一个新的行为,但是NSString实际上只是一个类簇的表面形式,因而为这样的类创建子类会非常困难。
问题来了:除了创建子类,还有什么方法为类添加新的行为?
利用Objective-C的动态运行时分配机制,你可以为现有的类添加新方法。这些新方法在OC中被称为类别(category)。


目标

理解类别的存在意义,知道怎么使用类别。


内容

类别是一种为现有类添加新方法的方式。
我们总是习惯把类别代码放在独立文件中,通常会以“类名称+类别名称”的风格命名,例如:“NSString+NumberConvenience”。当然,这只是习惯,并没有规定,但玩游戏别玩得太另类。

1.创建类别

@interface 部分

类别的声明看起来非常像类的声明:

@interface NSString (NumberConvenience)

- (NSNumber *) lengthAsNumber;

@end

上面的声明有两个有意思的特点:
1)类的名称后面是位于括号中的新名称,这意味着类别叫做NumberConvenience,而且它是添加给NSString类的。
翻译一下意思就是:我们为NSString类添加了一个名为NmberConvenience的类别。
理论上,只要保证类别名称的唯一,你可以向一个类中添加任意数量的类别。(但怎么保证类别名称的唯一呢?)
2)你可以指定添加类别的类(例中是NSString)和类别的名称(在本例中是NumberConvenience),还可以列出你要添加的方法。但是,不添加新的实例变量,因此这里不会像类声明那样包含实例变量的声明部分。你可以在类别中添加属性,但是不能添加实例变量,而且属性必须是@dynamic类型。添加属性好处就是可以使用点表达式了。

@implementation部分

@implementation NSString (NumberConvenience)

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

@end

类别的缺陷

类别有点叼,但不够叼。
两个局限性:
1)无法向类中添加新的实例变量。类别没有空间容纳实例变量。
2)名称冲突。这里指的是类别中的方法名与现有的方法重名,这就有点重写的意思了,当发生名称冲突时,类别具有更高的优先级。你的类别方法会完全取代初始方法,导致初始方法不再可用。有些程序员会在自己的类别中的方法名前加上一个前缀,(又是前缀)以确保不会发生名称冲突。

类别的优点

有优点,有缺点
类别主要有3个用途:

  1. 将类的实现代码分散到多个不同的文件或框架中。
  2. 创建对私有方法的前向引用。
  3. 向对象添加非正式协议(informal protocol)。

类扩展

类扩展(class extension),这个类别的特点之一就是不需要名字。
解释一下,我们之前都要为类命名并且会在定义@interface部分的时候使用这个名字,而这个特殊的类扩展类别不需要命名。
类扩展特点:

  1. 不需要名字
  2. 可以包含在你自己写的源代码类中使用
  3. 可以添加实例变量
  4. 可以将只读权限改成可读写的权限

例:

@interface ThingsNSObject

@property (assign)NSInteger thing1;
@property (readonly, assign) NSInteger thing2;

-(void)resetAllValues;

@end
@interface Things()                            //1.无名字
{
    NSInteger thing4;                          //2.添加实例变量
}

@property(readwrite, assign) NSInteger thing2;//3.可读写权限
@property(assign)NSInteger thing3;

@end

利用类别分散实现代码

以AppKit中的NSWindow类为例,如果将NSWindow类的所有代码都组织在一个文件中,即使是Cocoa的开发团队也会觉得这太庞大了,更不用说我们这些小小的开发员了,以所在该类的头文件中,可以看到:

@interface NSWindow(NSKeyboardUI)
@interface NSWindow(NSToolbarSupport)
@interface NSWindow(NSDrag)

利用类别,分开了键盘代码、工具栏代码、拖放功能代码,这样使程序员更容易阅读。

通过类别创建前向引用

Cocoa没有任何真正的私有方法。如果你知道对象支持的某个方法的名称,即使该对象所在的类的接口中没有声明该方法,你也可以调用。
如果编译器发现你调用对象的某个方法,但是却没有找到该方法的声明或定义,那么它会发出这样的错误提示:warning:’CategoryThing’ may not respond to ‘-setThing4:’。
如果你在使用方法之前没有声明它们,编译器就会发出警告。而修复编译器的警告是一种良好的习惯,那么该如果去做呢?
如果能够先定义一个方法然后再使用它,编译器将会找到你的方法定义,因而不会产生警告。但是,如果不方便这样做,或者你使用的是另一个类中尚未发布的方法,那么就只能通过类别来救急了。
只要在类别中声明一个方法,编译器就会表示:“好了,该方法已经存在,如果遇到编程人员使用该方法,我不会再警告了。”
苹果公司在APP Store的应用程序发布指导方针中就有一条,应用程序不能访问类里面的私有变量和方法。所以,如果你的应用程序里有这样的行为,很可能苹果不会让你的应用上架哦。

非正式协议和委托类别

委托(delegate)是一个类请求另一个对象执行某些工作。

响应选择器

当试图给一个对象发送一个它无法理解的消息时,你可能会遇到一个运行时错误:selector not recognized.
那么如何避免这个问题呢,那么首先应该检查对象,询问其能否响应该选择器。如果对象能够响应该选择器,则给他发消息。
什么时选择器(selector)?
选择器只是一个方法名称,但它以Objective-C运行时使用的特殊方式编码,以快速执行查询。
你可以使用@selector()编译指令圆括号中的方法名称来指定选择器。
例:Car类的setEngine:方法的选择器是:
@selector(setEngine:)
选择器可以被传递,可以作为方法的参数使用,甚至可以作为实例变量被存储。


总结+废话

介绍了类别。
类别可以向现有的类添加新方法,即使你没有这些类的源代码。
类别还可以将对象的实现代码分散到多个不同的源文件甚至多个不同的框架中。
类别可以声明非正式协议。非正式协议是NSObject的一个类别,它列出了对象可以响应的方法。非正式协议用于实现委托,委托是一种允许你轻松定制对象行为的技术。
学习了选择器,通过选择器可以在代码中指定特定的Objective-C消息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值