typedef struct objc_class *Class;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; //isa指针,指向metaclass(该类的元类)
#if !__OBJC2__
Class super_class //指向objc_class(该类)的super_class(父类)
const char *name //objc_class(该类)的类名
long version //objc_class(该类)的版本信息,初始化为0,可以通过runtime函数class_setVersion和class_getVersion进行修改和读取
long info //一些标识信息,如CLS_CLASS表示objc_class(该类)为普通类。ClS_CLASS表示objc_class(该类)为metaclass(元类)
long instance_size //objc_class(该类)的实例变量的大小
struct objc_ivar_list *ivars //用于存储每个成员变量的地址
struct objc_method_list **methodLists //方法列表,与info标识关联
struct objc_cache *cache //指向最近使用的方法的指针,用于提升效率
struct objc_protocol_list *protocols //存储objc_class(该类)的一些协议
#endif
} OBJC2_UNAVAILABLE;
typedef struct objc_object *id;
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
Class是一个指向objc_class(类)结构体的指针,而id是一个指向objc_object(对象)结构体的指针。
objec_object(对象)中isa指针指向的类结构称为objec_class(该对象的类),其中存放着普通成员变量与对象方法 (“-”开头的方法)。
objec_class(类)中isa指针指向的类结构称为metaclass(该类的元类),其中存放着static类型的成员变量与static类型的方法 (“+”开头的方法)。
元类(metaclass):在oc中,每一个类实际上也是一个对象。也有一个isa指针。因为类也是一个对象,所以也必须是另外一个类的实例,这个类就是元类(metaclass),即元类的对象就是这个类,元类保存了类方法的列表
在oc语言中,每一个类实际上也是一个对象。每一个类也有一个isa指针。每一个类也可以接收消息。因为类也是一个对象,所以也是另外一个类的实例,这个类就是元类(metaclass)。元类也是一个对象,所有的元类的isa指针都会指向一个根元类。根元类的isa指针又会指向他自己,这样就形成了一个闭环。
isa指针&父类&元类之间的关系一、isa指针的指向:
一个objc对象的isa指针指向他的类对象,类对象的isa指针指向他的元类,元类的isa指针指向根元类,所有的元类isa都指向同一个根元类,根元类的isa指针指向根元类本身。根元类super class父类指向NSObject类对象。根metaclass(元类)中的superClass指针指向根类,因为根metaclass(元类)是通过继承根类产生的。
实例对象的isa指针, 指向他的类对象,类对象的isa 指针, 指向他的元类。系统判断一个对象属于哪个类,也是通过这个对象的isa指针的指向来判断。对象中的成员变量,存储在对象本身,对象的实例方法,存储在他的isa 指针所指向的对象中。
对象在调用减号方法的时候,系统会在对象的isa指针所指向的类对象中找方法,这一段在kvo的实现原理中就能看到,kvo的实现原理就是系统动态的生成一个类对象,这个类是监听对象的类的子类,在生成的子类中重写了监听属性的set方法,之后将监听对象的isa指针指向系统动态生成的这个类,当监听对象调用set方法时,由于监听对象的isa指针指向的是刚刚动态生成的类,所以在执行的set方法也是子类中重写的set方法,这就是kvo的实现原理。同理,我们也可以通过rutime中的方法设置某个对象isa指针指向的类对象,让对象调用一些原本不属于他的方法。
类对象(class object)
类对象由编译器创建。任何直接或间接继承了NSObject的类,它的实例对象中都有一个isa指针,指向它的类对象。这个类对象中存储了关于这个实例对象所属的类的定义的一切:包括变量,方法,遵守的协议等等。因此,类对象能访问所有关于这个类的信息,利用这些信息可以产生一个新的实例,但是类对象不能访问任何实例对象的内容。类对象没有自己的实例变量。
创建p对象:Person * p = [Person new] ;
在创建一个对象p之前,在堆内存中就先存在了一个类(Person)(类对象),类对象在编绎时系统会为我们自动创建。在类第一次加载进内存时创建。
创建一个对象之后,在堆内存中会创建了一个p对象,该对象包含了一个isa指针的成员变量(第一个属性),isa指针则指向在堆里面存在的类对象, 在栈内存里创建了一个该类的指针p,p指针指向的是isa地址,isa指向Person.
对象方法的调用:
OC调用方法,运行的时候编译器会将代码转化为objc_msgSend(obj, @selector (selector)),在objc_msgSend函数中首先通过obj(对象)的isa指针找到obj(对象)对应的class(类)。在class(类)中先去cache中通过SEL(方法的编号)查找对应method(方法),若cache中未找到,再去methodLists中查找,若methodists中未找到,则去superClass中查找,若能找到,则将method(方法)加入到cache中,以方便下次查找,并通过method(方法)中的函数指针跳转到对应的函数中去执行。如果仍然找不到,则继续通过 super_class向上一级父类结构体中查找,直至根class
类方法的调用:
当我们调用某个类方法时,它首先通过自己的isa指针指向的objc_class中的isa指针找到元类,并从其methodLists中查找该类方法,如果找不到则会通过元类)的super_class指针找到父类的元类结构体,然后从methodLists中查找该方法,如果仍然找不到,则继续通过super_class向上一级父类结构体中查 找,直至根元类;
C语言函数编译的时候就会决定调用哪个函数,OC是一种动态语言,他会尽可能把代码的从编译链接推迟到运行时,这就是oc运行时多态。 给一个对象发送消息,并不会立即执行,而是在运行的时候在去寻找他对应的实现而OC的函数,属于动态调用过程,在编译期并不能决定真正调用哪个函数,只有在真正运行时才会根据函数的名称找到对应的函数来调用。
Objective-C的优点是它是动态的。动态能力有三种:
动态类-运行时确定类的对象
动态绑定-运行时确定要调用的方法
动态加载--运行时为程序加载新的模块
参考地址:
网友评论