OC中经常使用分类和协议,分类和协议在项目后期可能会经常用到,尤其是为某个已经成熟的模块添加功能的时候。因为:
1
,使用分类(
category
)可以以模块的方式向类添加方法.
2
,创建标准化的方法类表供其他人实现.
下面将对分类和协议做一个详细介绍。
一、分类
使用场景:
1
,有时候在处理类定义时,可能想要为其添加一些新方法.
2
,假如你参与了一个大型程序设计项目,并且作为项目的一部分,正在定义一个新类,新类中包含许多方法.你的任务是:为该类编写处理文件系统的方法.其他项目成员的任务负责以下方法:创建和初始化该类实例,对该类中的对象执行操作以及在屏幕上绘制该类对象的表示.
3
,假如你已经知道如何使库中的类(例如
fundation
的数组类,
NSArray
),并且意识到你希望该类实现了一个或者多个方法.当然,你可以编写
NSArray
类的新子类并实现新方法.
针对以上所有情况的使用的解决方案可以用:分类.
分类提供了一种简单的方式,用它可以将类的定义模块化到
相关方法的组
或者
分类中.分类还提供了一种扩展现有类定义的简便方式,并且不必访问类的源代码.也无需创建子类.
Fraction
原始代码
#import
@interface Fraction
:
NSObject
{
//
声明实例变量
int numerator
;
int denominator
;
}
//
将实例变量转换为属性
@property int numerator
,
denominator
;
//
定义实例方法
-(
void
)
setTo
:
(
int
)
n over
:
(
int
)
d
;
-(
Fraction *
)
add
:
(
Fraction *
)
fraction
;
-(
void
)
reduce
;
-(
double
)
convertToNumber
;
-(
void
)
print
;
@end
;
然后,从接口部分删除
add
:方法,并将其添加到新分类,同时添加其他三种要实现的数学运算.新
MathOps
分类的接口部分应该如下所示.
#import "Fraction
.
h"
#interface Fraction
(
MathOps
)
-(
Fraction *
)
add
:
(
Fraction *
)
f
;
-(
Fraction *
)
mul
:
(
Fraction *
)
f
;
-(
Fraction *
)
sub
:
(
Fraction *
)
f
;
-(
Fraction *
)
div
:
(
Fraction *
)
f
;
@end
;
注意:这既是接口部分的定义,也是现有接口部分的扩展.因此必须包括原始接口部分.这样编译器就知道
Fraction
类.
#interface Fraction
(
MathOps
):这句代码告诉编译器你正在为
Fraction
编写新的分类.而且新的分类的名称是
MathOps
.在这里没有列出实例变量,因为在以前定义的接口部分中已经这样做了.实际上如果再次列出的话,将接收到编译器发出的语法错误.
以上扩展接口的作用是:告诉编译器,你正在
MathOps
分类下为名为
Fraction
类添加扩展.
可以将所有方法的定义放在一个实现部分,也就是,可以在一个实现文件中定义
Fraction
.
h
接口中的所有方法和
MathOps
分类中的所有方法.也可以在单独的实现部分定义分类的方法.在第二种情况下,这些方法的实现部分还必须找出方法所属的分类.和接口部分一样,通过将类名称扩在类名称之后的圆括号内类确定方法所属的分类.
@implementation Fraction
(
MathOps
)
//code for category methods
@end
;
关于分类的一些注意事项:
1
.尽管分类可以访问原始类的实例变量,但是它不能添加自身的实力变量,如果需要添加变量,可以考虑创建子类.
2
.分类可以重载类中的另一个方法,但是通常不这样做,这种做法是拙劣的设计习惯.重载了一个方法之后,再也不能访问原来的方法.
3
.可以拥有多个分类,如果一个方法定义在多个分类中,该语句不会执行指定使用哪个分类.
4
,和一般接口不同的是,不必实现分类中的所有方法.
5
.通过使用分类添加新方法来扩展不仅会影响这个类,还会影响其所有子类.
二、协议:
协议是多个类共享一个方法列表.协议中列出的类没有响应的实现,有其他人来实现.
协议使用指定的名称定义一组多少有点相关的方法.这些方法通常有文档说明.所以你知道他们将如何执行.因此如果需要,可以在自己的类中定义他们的实现.
如果决定实现特定协议的所有方法,也就意味着要遵守这项协议.
定义一个协议:下面是一个例子
@protocol NSCopying
-
(
id
)
copyWithZone
:
(
NSZone *
)
zone
;
@end
;
如果你的类采用了
NSCopy
协议,则必须遵守实现名为
copyWithZone
的方法.通过在
@interface
行的一对
<>
内列出协议名
可以告诉编译器你正在使用一个协议.这项协议的名称放在类名和它的父类名称之后.
如果你的类实现了多项协议,协议之间用逗号分开.
协议不引用任何类型,它是无类的.任何类都可以遵守
Drawing
协议.
可以使用
ConformsToProtocal
:方法检查一个对象是否遵守某项协议.
例如有一个名为
currentObject
的对象,并且想要查看它时候是遵循
Drawing
协议.可以向他放松绘图消息
id currentObjec
;
if
(
[currentObjec
:
ConformsToProtocal
:
@protoco
(
Drawing
)
:
==
YES]
)
{
....
//DO SOMETHING
;
}
这里使用一个专用的
@protocol
指令用于获取一个协议名称.并产生一个
Protocol
对象.
通常在类型名称之后的
<>
中添加协议名称,可以借助编译器来检查变量的一致性.
id
这告诉编译器
currentObject
将包含遵守
Drawing
协议的对象.如果这个对象遵循多个协议,可以用逗号隔开.
定义一项协议的时候,可以扩展现有的协议.
说明
Drawwing3D
协议也使用了
Drawing
协议.
最后分类也可以采用一项协议
@interface Fraction
(
Stuff
)
此处
Fraction
类拥有一个
Stuff
分类,这个分类采用了
NSCopy
,
NSCoding
两个协议.
和类名称一样,协议名称也是唯一的.
非正式协议:
分正式协议实际上就是一个分类.列出了一组方法,但是并没有实现他们.每个人都继承相同的跟对象,因此非正式分来通常是为跟类定义的.又是非正式协议又叫做抽象协议.
声明分正式协议的类并不自己实现这些方法,并且选择实现这些方法的子类需要在他的接口部分重新声明这些方法.同时还要是实现这些方法中的一个或多个,
合成对象:
你已经学习了通过派生子类和分类技术类扩展类定义的集中方法.还有一项涉及定义一个包含其他类的一个或者多个对象的技术.
@interface Square
:
NSObject
{
Rectangle * rect
;
}
-(
int
)
setSide
:(
int
)
s
;
@end