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

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

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

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

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

我要捐赠: 点击捐赠

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


多态这个其他语言也有。

动态类型有类似的。但不全然同样。

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

多态可以使来自不同类的对象定义同样名称的方法

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

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


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

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

 

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

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

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


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

比如: “几何图形” 类的“画图” 方法, 在它的子类“椭圆形” 和“三角形” 中也都有“画图” 的方法,
可是“画图” 方法功能都不同。

几何图形类图
Graphics(几何图形) 类是Ellipse(椭圆形) 类和Triangle(三角形) 类的父类, 
Ellipse和Triangle重写了onDraw方法。

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


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没有不论什么关系。
务必注意,声明中并没有使用星号

我们再举一个样例来说明动态的概念:
矢量和标量是数学和物理学应用中经经常使用到的两个概念;
矢量” 即有方向和大小的量, 如物理学中的“力” 。 
标量为没有方向仅仅有大小量, 如物理学中的“功”。

以下从程序的角度来实现这两个概念。
先定义两个类: “矢量” 和“标量” 类。

 

Objective-C路成魔【11-多态性、动态类型和动态绑定】
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反对非必要的使用异时常。

版权声明:本文博主原创文章。博客,未经同意不得转载。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/117079.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • CouchBase_couchbase数据库

    CouchBase_couchbase数据库1.CouchBase是什么简单来说CouchBase是一款开源的,分布式的nosql数据库,主要用于分布式缓存和数据存储领域。能够通过managecache提供快速的亚毫米级别的k-v存储操作,

  • java volatile关键字的作用_java volatile关键字作用及使用场景详解

    java volatile关键字的作用_java volatile关键字作用及使用场景详解1.volatile关键字的作用:保证了变量的可见性(visibility)。被volatile关键字修饰的变量,如果值发生了变更,其他线程立马可见,避免出现脏读的现象。如以下代码片段,isShutDown被置为true后,doWork方法仍有执行。如用volatile修饰isShutDown变量,可避免此问题。publicclassVolatileTest3{staticclassW…

  • git版本管理工具介绍(git管理工具有哪些)

    Git 是一个分布式版本控制工具,它的作者 LinusTorvalds是这样给我们介绍Git —— Thestupidcontenttracker(傻瓜式的内容跟踪器)1、Git背景Git最初由LinusTorvalds编写,用于Linux内核开发的版本控制工具。Git与常用的版本控制工具CVS、Subversion等不同,它采用了分布式版本库的方式,不必服务器端软

  • 什么品种的猫最受欢迎?Python爬取猫咪网站交易数据[通俗易懂]

    什么品种的猫最受欢迎?Python爬取猫咪网站交易数据[通俗易懂]本篇文章是关于某化妆品企业的销售分析。从分析思路开始带大家一步步地用python进行分析,找出问题,并提出解决方案的整个流程。以下文章来源于修炼Python作者:叶庭云Python爬虫、数据分析、网站开发等案例教程视频免费在线观看https://space.bilibili.com/523606542一、前言看到可爱的猫咪表情包,总是会忍不住收藏,晒部分图如下:认识的一些朋友也养了猫,比如橘猫、英短、加菲猫之类的,看他们发朋友圈撸猫,老羡慕了,猫咪真的太可爱啦。发.

  • 树莓派的产品定位,以及探讨与ARM开发板的区别

    树莓派的产品定位,以及探讨与ARM开发板的区别目录一、树莓派的几点认识二、树莓派的定位,与ARM开发板有不同的目标用户三、树莓派与嵌入式开发的区别1、树莓派能不能做为嵌入式的ARM开发板?2、树莓派能不能进行嵌入式开发与学习?四、树莓派上的实践一、树莓派的几点认识1、树莓派是一款基于Linux系统的单板机电脑。它由英国的树莓派基金会所开发,目的是以低价硬件及自由软件刺激在学校的基本的计算机科学教育。树莓…

  • Python暴力激活成功教程wifi密码

    Python暴力激活成功教程wifi密码今天给大家分享一个使用Python激活成功教程WiFi密码的代码,这个代码也是非常简单,这里需要用Python中的pywifi这个库,所以需要在DOS命令下安装这个库,同样使用pipinstallpywifi,很简单就安装成功了,我用的是Python3,所以各位看的时候需要注意这一点。接下来我们一步一步分析主要代码,后面同样附上完整的代码。对了,需要注意一点,就是电脑必须是要用无线网卡的。首先我们…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号