在前段时间的OC学习中,总是不断地提到:调用一个类的方法,其实这种说法对于OC语言来说是不正确的。
对于OC来说,调用方法被称为:向这个类(类方法)或者类对象(对象方法)发送消息。那么,疑问就来了,为什么OC非要说是发送消息,而不是方法调用呢?这要从程序运行的底层去分析。
当程序编写完成,进入编译阶段时,例如[person say],当它编译的时候就变成了:
objc_msgsend(person @selector(say));(简单了解一下)
对于这个底层代码的理解应该是,向person这个对象发送say的消息。所以OC称调用方法为向某个接受者发送某个消息。
在学习C语言的时候,大家可能都有这个编程经历,那就是C语言程序在编译的时候就去寻找函数的具体实现,如果这个函数没有实现,程序编译就不能通过。但是OC可以成功编译,运行时才会报错。
OC的动态特性
给大家介绍一下OC所具有的动态特性:动态类型、动态绑定、动态加载。学习它们,有助于深入了解OC程序编译运行的具体过程。通过下面的学习,相信大家会对OC有一个新的认识。
动态类型
在OC中提到动态类型,简单的理解,指的就是:id类型。它可以表示任何类的对象。当我们无法知道数据的具体类型时,我们就可以将数据直接存储到id类型的对象中。
但是需要说明的是id是动态类型绝不仅仅因为它能够存储任何类的对象。主要的原因是:由于id类型在程序编译时无法确定其中具体存储的对象类型,只有在程序通过编译到达运行阶段时,才能确定id类型对象所存储的具体对象的类型。
简单的总结上面所说,动态类型就是指id类型,而说它是动态类型是因为只有在程序运行时才能确定id对象中存储的具体类型。
动态绑定
动态绑定,是基于动态类型的,简单的理解就是:只有在程序运行时才能确定对象的具体属性和方法,进而进行绑定。
当程序中包含id动态类型时,编译过程无法确定id类型代表哪个类的对象,也就无法确定id类型调用的是哪个方法。在程序编译阶段,编译器会在你的工程文件中查找id对象调用方法的方法名是不是存在,如果存在,编译通过;反之,程序报错,提示你没有找到对象的方法。
(提示:对于id这种动态类型,它本身并不属于类对象,所以在程序中如果使用id对象通过点语法的方法调用属性,程序会报错。即使程序逻辑上是对的,但是首先要弄清楚的一点是:id不是类对象,没有属性,更不用说通过点语法来调用某个类对象的属性)。
下面,举一个例子来具体感受一下OC的动态类型和动态绑定:1、 先独立创建一个OC工程,例如Demo;2、 在工程中,创建一个类,例如Person类;在Person.h文件中:
#import <Foundation/Foundation.h>
@interface Person : NSObject
-(void)exercise;
@end
在Person.m文件中:
#import "Person.h"
@implementation Person
-(void)exercise{
NSLog(@"I am running!!!");
}
@end
在main.m文件中:
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
Person * person=[[Person alloc] init];
id person1=[[NSObject alloc] init];
[person1 exercise];
[person exercise];
return 0;
}
做完以上的工作,代码就编写完成了,下面亲身感受一下,这段代码中体现到的动态特性。
首先,先编译一下我们的工程(快捷键:“command+B”),发现程序并没有报错,编译成功。
下面,运行一下程序(快捷键:“command+R”),发现程序崩溃,反馈给一堆错误数据;(在初学一门语言时,出错是很正常的,我们要从不断的出错过程中,总结错误的原因,不断的积累,不断的提高调程序的能力)
从错误中看到:'-[NSObject exercise]: unrecognized selector sent to instance 0x1006000a0'(提示我们没有找到这个方法的实现)
上边的例子中,我们可以看到,虽然编译没问题,但是一运行就报错。这充分体现出了OC的动态特征。
在编译时,由于OC无法确定id类型的person1具体指的是哪个类的对象。所以无法确定是否能像这个对象发送exercise消息,而在本工程中,的确含有这个方法,所以程序编译通过。
这些不确定只有等到程序运行阶段,确定了person1对象的类型以后,一切就都能知道了,所以在程序运行时,发现person1对象所属的类中并没有exercise的方法,程序报错。
(在程序运行时遇到id类型的对象,首先会判断它具体指的是哪个类,拿上边的例子来说,id具体指的是NSObject这个类,所以首先会去NSObject这个类中查找看看是否有exercise这个方法,如果没有,再去它的父类中去找(一层层往上找),但是我们这个例子中,NSObject类是一个基类,他没有父类,所以程序因为找不到这个方法报错)
动态加载
动态加载,简单的理解就是在需要时才会加载。
动态加载在实际编程过程中哪些情况体现出动态加载呢?例如,用类别这种方式给某个类添加一些方法之后,只有当程序运行过程中用到类别文件中的方法时才会加载。