Objective-C之成魔之路【12-分类与协议】

郝萌主倾心贡献,尊重作者的劳动成果,请勿转载。

如果文章对您有所帮助,欢迎给作者捐赠,支持郝萌主,捐赠数额随意,重在心意^_^

我要捐赠: 点击捐赠

Cocos2d-X源码下载:点我传送

分类与协议是OC比较有特色的部分。

从表面来看,

分类呢有点类似抽象方法在抽象类中(C++或者Java里的那个抽象类概念)。

协议类似接口(Java语言那个接口),但是又不能“一视同仁”。

分类概念

分类(Category) 允许向一个类文件中添加新的方法声明,

它不需要使用子类机制, 并且在类实现的文件中的同一个名字下定义这些方法。

其语法举例如下:

#import
"ClassName.h"

@interface ClassName ( CategoryName )

// 方法声明

@end

分类实例

上一篇多态性介绍中曾经使用过Vector和Scalar的例子,

下面我们为Vector增加“减”sub的方法。

Vector+sub.h文件

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

@interface Vector (sub) 

-(Vector *) sub: (Vector *) v; 

@end

Vector+sub.m文件

#import "Vector+sub.h"

@implementation  Vector (sub) 

-(Vector *) sub: (Vector *) v {
	Vector *result = [[Vector alloc] init];
	[result setVec1: vec1 - [v vec1] andVec2: vec2 - [v vec2]];
	return result;
}

@end

调用的main函数

#import <Foundation/Foundation.h>
#import "Vector+sub.h"

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

	Vector *vecA =[[Vector alloc] init];
	Vector *vecB =[[Vector alloc] init];
	id result;

	//set the values
	[vecA setVec1: 3.2 andVec2: 4.7];
	[vecB setVec1: 32.2 andVec2: 47.7];

	// print it
	[vecA print];
	NSLog(@" + ");
	[vecB print];
	NSLog(@" = ");
	result = [vecA add: vecB];
	[result print];

	[vecA print];
	NSLog(@" - ");
	[vecB print];
	NSLog(@" = ");
	result = [vecA sub: vecB];
	[result print];

	// free memory
	[vecA release];
	[vecB release];
	[result release];
    return 0;
}

其中result = [vecA add: vecB] 中的add: 是Vector类原有的方法,

result = [vecA sub: vecB] 中的sub: 是Vector分类添加的方法。

分类是在Java和C++等面向对象的语言中没有的概念,

分类本质上是通过Objective-C的动态绑定而实现的。

通过分类使用能够达到比继承更好的效果。

从上面这个例子可以看到,分类提供了一种简单的方式,

用它可以将类的定义模块化到相关方法的组或分类中。

它还提供了扩展现有类定义的简便方式,并且不必访问类的源代码,也不需要创建子类。

#import "Fraction.h"

@interface Fraction (MathOps)

-(Fraction *) add: (Fraction *) f;

-(Fraction *) mul: (Fraction *) f;

-(Fraction *) sub: (Fraction *) f;

-(Fraction *) div: (Fraction *) f;

@end

注意,这既是接口部分的定义,也是现有接口部分的扩展。

因此,必须包括原接口部分,这样编译器就知道Fraction类。

按照惯例,作为分类的.h和.m文件的基本名称是由类的名称紧接着分类的名称。

例如:FractionMathOps.m;

一些程序员使用符号“+”来分隔类和分类的名称,比如Fraction+MathOps.h。不过不建议这样命名。

类的扩展:

创建一个未命名的分类,且在括号“()”之间不指定名字,这是一种特殊的情况。

这种特殊的语法定义为类的扩展。

未命名类中声明的方法需要在主实现区域实现,而不是在分离的实现区域中实现。

未命名分类是非常有用的,因为它们的方法都是私有的。

如果需要写一个类,而且数据和方法仅供这个类本身使用,未命名类比较合适。

关于分类的注意事项:

分类可以覆写该类中的另一个方法,但是通常认为这种做法是做虐的设计习惯。所以需要注意:

第一、覆写了一个方法后,再也不能访问原来的方法。(如果确实需要覆写方法,正确的选择可能是创建子类。)

第二、通过使用分类添加新的方法来扩展类不仅仅影响这个类,同时也会影响它的所有子类。

第三、对象/分类命名对必须是唯一的。因为大家使用的名称空间是程序代码、库、框架和插件共享的。

协议(Protocol )

与Java的Interface(接口 )或者C++的纯虚类相同,就是用来声明接口的。

协议只是定义了方法的列表,协议不负责实现方法,目的是让别的类来实现。

以Graphics协议为例:

Graphics中定义了onDraw方法, 但是我们仔细分析一下onDraw方法不能实现的,

作为Graphics(几何图形)它无法知道它的子类如何绘制图形,

它只能规定绘制图名字为onDraw签名和返回值等信息, 但不能给出具体的实现,

因此Graphics(几何图形) 不应该设计成为类而应该设计成为协议。

@protocol Graphics

-(void) onDraw;

@end

协议只有接口部分, 没有实现部分, 所以没有m文件, 关键字@protocol ,

协议可以继承别的协议, 协议中不能定义成员变量。

协议实现类Ellipse

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

@interface Ellipse:NSObject <Graphics> {

}

@end
#import "Ellipse.h"

@implementation Ellipse

-(void)onDraw {
	NSLog(@"绘制椭圆形");
}

@end

协议实现类Triangle

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

@interface Triangle:NSObject <Graphics> {

}

@end
#import "Triangle.h"

@implementation Triangle

-(void)onDraw {
	NSLog(@"绘制三角形");
}

@end

代码说明:

协议的实现是在类声明的父类之后, 加上<Graphics>,
与类的单个继承不同,

协议可以实现多个, 表示要实现这个协议,

如果有多个协议要实现用“,” 号分隔: <P1,P2>。

调用的main函数

#import <Foundation/Foundation.h>
#import "Graphics.h"
#import "Ellipse.h"
#import "Triangle.h"

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

	id graphics;
	graphics = [[Ellipse alloc] init];
	[graphics onDraw];
	[graphics release];

	graphics = [[Triangle alloc] init];
	[graphics onDraw];
	[graphics release];

    return 0;
}

从上面的例子可以看出:

协议是多个类共享的一个方法列表。

协议中列出的方法没有相应的实现,计划由其他人来实现。

协议提供了一种方式,用指定的名称定义一组多少有点相关的方法。

如果决定实现特定协议的所有方法,也就意味着要遵守(confirm to)或采用(adopt)这项协议。

定义一个协议很简单:只要使用@protocol指令,然后跟上你给出的协议名称。

例如:

@protocol NSCoping

- (id) copyWithZone: (NSZone *) zone;

@end

这里再提到两个重要的协议:

NSCopying

NSCoding

如果你的类采用NSCopying协议,则必须实现copyWithZone:的方法

通过在@interface行的一对尖括号(<....>)内列出协议名称,可以告知编译器你正在采用的一个协议。

这项协议的名称放在类名和它的父类名称之后,

例如:

@interface AddressBook:NSObject <NSCoping>

如果你的类采用多项协议,只需把它们都列在尖括号中,并用逗号分开,例如:

@interface AddressBook:NSObject <NSCoping, NSCoding>

如果你定义了自己的协议,那么不必由自己实现它。

如果一个类遵守NSCoping协议,则它的子类也遵守NSCoping协议

(不过并不意味着对该子类而言,这些方法得到了正确的实现)。

@required和@optional关键字

@protocol MyProtocol

- (void) requiredMethod;

@optional

- (void) anOptionalMethod;

- (void) anotherOptionalMethod;

@required

- (void) anotherRequiredMethod;

@end

注意,这里使用了@optional指令。该指令之后列出的所有方法都是可选的。

@required指令之后的是需要的方法。

注意,协议不引用任何类,它是无类的(classless)。

任何类都可以遵循MyProtocol协议。

可以使用conformsToProtocol:方法检查一个对象是否遵循某些协议。

例如:

if ([currentObject conformsToProtocol: @protocol (MyProtocol)] == YES) {

...

}

这里使用的专用@protocol指令用于获取一个协议名称,并产生一个Protocol对象。

conformsToProtocol:方法期望这个对象作为它的参数。

通过在类型名称之后的尖括号中添加协议名称,可以借助编译器来检查变量的一致性。

例如:

id <Drawing> currentObject;

这告知编译器currentObject将包含遵守Drawing协议的对象。

如果这个变量保存的对象遵守多项协议,则可以列出多项协议 ,

如以下代码:

id <NSCoping, NSCoding> myDocument;

定义一项协议时,可以扩展现有协议的定义。

如:

@protocol Drawing3D <Drawing> Drawing3D 协议也采用了Drawing协议。

最后要说的是,分类也是可以采用一项协议。

和类名一样,协议名必须是唯一的。

代理:

协议也是一种两个类之间的接口定义。定义了协议的类可以看做是将协议定义的方法代理给了实现它们的类。

非正式(informal)协议:是一个分类,列出了一组方法但并没有实现它们。

因此,非正式分类通常是为根类定义的。有时,非正式协议也称为抽象(abstract)协议。

非正式协议实际上只是一个名称下的一组方法。

声明非正式协议的类自己并不实现这些方法,

并且选择实现这些方法中的子类需要在它的接口部分重新声明这些方法,

同时还要实现这些方法中的一个或多个。

注意:前面描述的@optional指令添加到了Objective-C 2.0语言中,用于取代非正式协议的使用。

合成对象:

可以定义一个类包含其他类的一个或多个对象。

这个新类的对象就是所谓的合成(composite)对象,因为它是由其他对象组成的。

子类依赖于父类,改变了父类有可能会使得子类中的方法不能工作。

时间: 2024-08-06 23:48:58

Objective-C之成魔之路【12-分类与协议】的相关文章

Objective-C之成魔之路【1-序章】

重开发者的劳动成果,请勿转载 C语言首创于AT&T实验室, UNIX OS的发展促进了C语言的快速普及,UNIX OS几乎完全是由C语言编写的. Brad J.Cox在20世纪80年代早期设计了Objective-C语言,它以一种叫做SmallTalk-80的语言为基础. SmallTalk-80可以参考: http://zh.wikipedia.org/wiki/Smalltalk Objective-C在C语言的基础上加了一层,这意味着对C进行了扩展, 从而创造出一门新的程序设计语言,支持面

Objective-C之成魔之路【3-数据类型】

重开发者的劳动成果,请勿转载 数据类型. 运算符和表达式在任何的计算机语言中都比较重要的, 在面向对象的Objective-C语言中, 除了常规的基本类型, 还有对象类型等. 运算符和表达式完全遵守C语言规范. Objective-C数据类型可以分为:基本数据类型. 对象类型和id类型. 基本数据类型有: int. float. double和char类型. 对象类型就是类或协议所声明的指针类型, 例如: NSAutoreleasePool * pool, 其中NSAutoreleasePool

Objective-C之成魔之路【15-Foundation框架】

郝萌主倾心贡献,尊重作者的劳动成果,请勿转载. 如果文章对您有所帮助,欢迎给作者捐赠,支持郝萌主,捐赠数额随意,重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源码下载:点我传送 概述 Mac OS X开发会使用 Cocoa框架, 它是一种支持应用程序提供丰富用户体验的框架, 它实际上由: Foundation和Application Kit(AppKit)和 Core Data框架组成. iOS开发, 会使用 Cocoa Touch框架, 它实际上由: Foundation.Core

Objective-C之成魔之路【11-多态性、动态类型和动态绑定】

郝萌主倾心贡献,尊重作者的劳动成果,请勿转载. 如果文章对您有所帮助,欢迎给作者捐赠,支持郝萌主,捐赠数额随意,重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源码下载:点我传送 多态这个其它语言也有.动态类型有类似的,但不完全相同. 动态绑定别的语言也有类似,但没有objective-c用的这么多. 多态能够使来自不同类的对象定义相同名称的方法. 动态类型能使程序直到执行时才确定对象所属的类. 动态绑定则能使程序直到执行时才确定实际要调用的对象方法. 多态性是指在父类中定义的成员变量和

Objective-C之成魔之路【5-选择结构】

重开发者的劳动成果,请勿转载 Objective-C中的控制语句有以下几类: ? 分支语句: if-else, switch ? 循环语句: while, do-while, for ? 与程序转移有关的跳转语句: break, continue, goto 对于任何程序语言来说,有能力进行判断是一项基本特性. if-else语句 分支语句提供了一种控制机制, 使得程序的执行可以跳过某些语句不执行, 而转去执行特定的语句. 1. 条件语句 if-else 2. 多分支语句 switch 条件语句

Objective-C之成魔之路【17-内存管理】

郝萌主倾心贡献,尊重作者的劳动成果,请勿转载. 如果文章对您有所帮助,欢迎给作者捐赠,支持郝萌主,捐赠数额随意,重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源码下载:点我传送 内存管理关心的是清理(回收)不用的内存,以便内存能够再次利用. 提供给Objective-C程序员的基本内存管理模型有以下三种: 1)自动垃圾收集.(iOS运行环境并不支持垃圾收集,在这个平台开发程序时没有这方面的选项,只能用在Mac OS X 程序上开发.这个机制挺恶心的,用mac电脑的人知道,当内存不足的时

Objective-C之成魔之路【16-使用文件】

郝萌主倾心贡献,尊重作者的劳动成果.请勿转载. 假设文章对您有所帮助,欢迎给作者捐赠.支持郝萌主,捐赠数额任意.重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 语言的设计主要是被应用于实践, 而Objective-C应用最广的地方就是Mac OS X或iOS的Foundation框架. Foundation框架同意你利用文件系统对文件或文件夹运行基本操作,这些基本操作是由NSFileManager类提供的. 使用NSFileHandle类提供的方法,能够打开文件并对

Objective-C之成魔之路【10-继承性】

重开发者的劳动成果,请勿转载 继承性是面向对象的重要概念之一, 子类能够继承父类的某些方法和成员变量. 作用域限定符为private的成员变量是不可以被继承的. 子类还可以重写父类的方法. 当然,这一切要从根类开始: 没有父类的类,位于类层次结构的最顶层,称为根(Root)类. NSObject是层次结构的最顶端(也就是它上面没有任何类),因此称为根类. 如果使用术语,可以将类称为子类和父类.同样,也可以将类称为子类和超类. 需要注意的是,要在子类中直接使用实例变量,必须先在接口部分声明. 在实

Objective-C之成魔之路【1-Objective-C特点】

重开发者的劳动成果,请勿转载 Objective-C与其它面向对象有这明显的不同, 它有这自己鲜 明的特色, 下面我们从这个方法介绍它的特点: 兼容性. 字 符串. 类. 方法. 属性. 协议和分类. 1.兼容性 Objective-C可以说是一种面向对象的C语言, 在Objective-C的代码中可以有C和C++语句, 它可以调用 C的函数, 也可以通过C++对象访问方法. 2.字符串 Objective-C通常不使用C语言风格的字符串. 大多数情况下是使用Foundation框架的NSStr

Objective-C之成魔之路【8-訪问成员变量和属性】

郝萌主倾心贡献,尊重作者的劳动成果.请勿转载. 假设文章对您有所帮助,欢迎给作者捐赠,支持郝萌主,捐赠数额任意,重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 訪问成员变量 从面向对象的封装角度考虑问题, 要想訪问类中的成员变量, 是要通过方法訪问的, 成员变量前面要有作用域限定符(protected, public, private) , 这些存取权限修饰符我们将在后面介绍. 成员变量的訪问, 是通过读取方法(getter) 和设定方法(setter). 訪问成员