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

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

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

我要捐赠: 点击捐赠

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

多态这个其它语言也有。动态类型有类似的,但不完全相同。

动态绑定别的语言也有类似,但没有objective-c用的这么多。

多态能够使来自不同类的对象定义相同名称的方法。

动态类型能使程序直到执行时才确定对象所属的类。

动态绑定则能使程序直到执行时才确定实际要调用的对象方法。

多态性是指在父类中定义的成员变量和方法被子类继承之后, 可以具有不同的数据类型或表现出不同的行为。

这使得同一个变量和方法在父类及其各个子类中具有不同的表现形式。

简单点说:相同的名称,不同的类,系统总是携带有关“一个对象属于哪个类”这样的信息。

该信息能使系统在运行时做出这些关键性的决定,而不是在编译时。

这种使不同的类共享相同方法名称的能力就称为多态。

我们通过一个例子理解什么是多态:

例如: “几何图形” 类的“绘图” 方法, 在它的子类“椭圆形” 和“三角形” 中也都有“绘图” 的方法,

但是“绘图” 方法功能都不同。

几何图形类图

Graphics(几何图形) 类是Ellipse(椭圆形) 类和Triangle(三角形) 类的父类,

Ellipse和Triangle重写了onDraw方法。

Graphics.h文件

Graphics类在h文件中定义onDraw, Graphics类应该无法实现这

个onDraw, 这是因为在几何图形中是无法知道要绘制的是椭

圆形和三角形, 这个方法应该是类似Java中的抽象方法, 或者是

C++中的虚方法。 

@interface Graphics : NSObject {

}

-(void) onDraw;

@end

Ellipse类的h和m文件

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

@interface Ellipse : Graphics {

}

@end
#import "Ellipse.h"

@implementation Ellipse

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

@end

Triangle类的h和m文件

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

@interface Triangle : Graphics {

}

@end
#import "Triangle.h"

@implementation Triangle

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

@end

调用的main函数

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

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

	Graphics *graphics;
	graphics = [[Ellipse alloc] init];
	[graphics onDraw];
	[graphics release];

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

    return 0;
}

运行结果:

绘制椭圆形

绘制三角形

动态类型和动态绑定

id 是泛类型 (generic data type), 可以用来存放各种类型的对象,

使用 id 也就是使用“动态类型”。

上面的例子改写下:

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;

}

把Graphics *改成id类型, 程序运行的结果没有任何影响。

由于动态类型的关系,id 在执行时,

Objective-C 的执行环境会找出该 id 所代表的原来类型,

所以根本没有所谓的转型。

id 并不是自动的转换成 Ellipse和 Triangle的父类,而是在执行期间,

由执行环境辨认出 id 实际代表的类型为Ellipse还是Triangle。

因此在这个例子中id 与Graphics没有任何关系。

务必注意,声明中并没有使用星号。

我们再举一个例子来说明动态的概念:

矢量和标量是数学和物理学应用中经常用到的两个概念;

矢量” 即有方向和大小的量, 如物理学中的“力” 。

标量为没有方向只有大小量, 如物理学中的“功”。

下面从程序的角度来实现这两个概念。

先定义两个类: “矢量” 和“标量” 类。

Vector.h文件

#import <Foundation/Foundation.h>

@interface Vector : NSObject {
	double vec1;
	double vec2;
}

@property double vec1,vec2;
-(void)print;
-(void)setVec1:(double)v1 andVec2:(double) v2;
-(Vector *)add:(Vector *)v;

@end

Vector.m文件

#import "Vector.h"

@implementation Vector
@synthesize vec1,vec2;
-(void) setVec1:(double) v1 andVec2:(double)v2 {
	vec1 = v1;
	vec2 = v2;
}
-(Vector *)add:(Vector *)v {
	Vector *result = [[Vector alloc] init];
	[result setVec1:vec1 + [v vec1] andVec2: vec2 + [v vec2]];
	return result;
}

-(void)print {
	NSLog(@"%g,  %g",vec1,vec2);
}

@end

Scalar.h文件

#import <Foundation/Foundation.h>

@interface Scalar : NSObject {
	double scal;
}
@property double scal;
-(void)print;
-(void)setScal:(double)sval;
-(Scalar *)add:(Scalar *)s;

@end

Scalar.m文件

#import "Scalar.h"

@implementation Scalar

@synthesize scal;

-(void)print {
	NSLog(@"%g", scal);
}

-(void)setScal:(double)sval {
	scal = sval;
}

-(Scalar *)add:(Scalar *)s {
	Scalar *result  = [[Scalar alloc] init];
	[result setScal:scal + [s scal]];
	return result;
}

调用的main函数

#import <Foundation/Foundation.h>

#import "Vector.h"
#import "Scalar.h"

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

	Scalar *scA =[[Scalar alloc] init];
	Scalar *scB =[[Scalar alloc] init];

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

	id scAandB;
	id vecAandB;

	[scA setScal: 10.5];
	[scB setScal: 13.1];
	[vecA setVec1: 3.2 andVec2: 4.7];
	[vecB setVec1: 32.2 andVec2: 47.7];

	[vecA print];
	NSLog(@" + ");
	[vecB print];
	NSLog(@" = ");
	vecAandB = [vecA add: vecB];
	[vecAandB print];
	[scA print];
	NSLog(@" + ");
	[scB print];
	NSLog(@" = ");
	scAandB = [scA add: scB];
	[scAandB print];

	[scA release];
	[scB release];
	[scAandB release];
	[vecA release];
	[vecB release];
	[vecAandB release];

	return 	0;
}

运行结果:

3.2,
4.7

+

32.2, 47.7

=

35.4, 52.4

10.5

+

13.1

=

23.6

代码说明:

scAandB和vecAandB对象都是动态类型, 都可调用以"print"和"add"方法。

注意:

虽然id类型可以任何类型的对象,但是不要滥用 ,

如果能够确定对象数据类型时候, 要使用“静态类型” ,

为什么要使用静态类型:

1)将一个变量定义为特定类的对象时,使用的是静态类型。

“静态”指的是对存储在变量中对象的类型进行显示声明。

这样存储在这种形态中的对象的类是预定义的,也就是静态的。

使用静态类型时,编译尽可能确保变量的用法在程序中始终保持一致。

编译器能够通过检查来确定应用于对象的方法是由该类定义的还是由该类继承的,否则它将显示警告信息。

也就是说“静态类型”
在编译阶段检查错误, 而不是在执行阶段。

2)使用静态类型的另一个原因是程序可读性好。。

动态类型的参数和返回类型:

如何使用动态类型来调用一个方法,需要注意如下规则:

如果在多个类中实现名称相同的方法,那么每个方法都必须符合各个参数的类型和返回值类型。

这样编译器才能为消息表达式生成正确的代码。编译器会对它所遇到的每个类声明执行一致性检查。

当一个方法选取对象作为它的参数,而另一个方法选取浮点数作为参数时,

或者一个方法以对象作为返回值,而另一个以整型数作为返回值。

编译器可能生成不正确的代码来向方法传递参数或处理返回值。

处理动态类型的方法:

  1. -(BOOL) isKindOf:class-object(判断对象是否是class-object或其子类的成员)
  2. -(BOOL) isMenberOfClass:class-object(判断对象是否是class-object的成员)
  3. -(BOOL) respondsToSelector:selector(判断对象是否能够响应selector所指定的方法)
  4. +(BOOL) instancesRespondToSelector:selector(判断指定的类实例是否能响应selector所指定的方法)
  5. +(BOOL) isSubclassOfClass:class-object(判断对象是否是指定类的子类)
  6. -(id) performSelector:selector(应用selector指定的方法)
  7. -(id) performSelector:selector withObject:object(应用selector指定的方法,传递参数object)
  8. -(id) performSelector:selector withObject:object1 withObject:object2(应用selector指定的方法,传递参数object1和object2

可以对一个方法名应用@selector指令。

例如:@selector (alloc)为名为alloc的方法生成一个SEL类型的值,该方法是从NSObject类继承的。

记住,测试包含继承的方法,并不是只测试直接定义在类中的方法。

performSelector:方法和它的变体允许你向对象发送消息,这个消息可以是存储在变量中的selector。

在iOS中,respondsToSelector:方法广泛用于实现委托(delegation)的概念。

为了让系统能够检查你确实实现了特定的方法,

使用respondsToSelector:判断是否可以将事件的处理委托给你的方法。

如果你没有实现这个方法,它会自己处理该事件,按定义的默认行为来执行。

使用@try处理异常

@try:如果块中的某一语句抛出异常,执行不会终止,而是立即跳到@catch块继续执行

@catch:处理异常,可行的执行顺序是记录出错信息,清除和终止执行。

@finally:使用@finally块包含是否执行抛出异常的@try块中的语句代码;

@throw:允许你抛出自己的异常,

这些概念和java的或者其它语言差不多。

一般来说,需要考虑更好的编程实践,

应该在错误发生前做系统的全面的覆盖性测试,而不是在错误发生后捕获异常。

抛出异常会使用大量的系统资源,Apple反对非必要的使用异常。

时间: 2024-07-30 13:42:56

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

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

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

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之成魔之路【15-Foundation框架】

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

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). 訪问成员