oc-理解对类、对象等概念

2019-08-11  本文已影响0人  雨天多久就

OC是基于C语言的面向对象的语言。C语言中没有对象的概念,为了便于开发者理解和使用面向对象的思想,OC将C语言中的结构体进行了封装,创造了oc中的类和对象等概念。

Class

typedef struct objc_class *Class;

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

从定义看,Class其实就是一个结构体objc_class的指针Class指向了结构体objc_class的内存地址。

而结构体objc_class的里面,包含了:

对象

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

代表了一个类的实例对象。
她只有一个isa指针,指向了它所属的类。
当用这个类调用方法的时候,实例对象会通过isa指针找到它所属的类,然后在该类的方法缓存列表里去查找对应的方法,如果找不到就去方法列表里找,如果还找不到就去父类的里面找……

对于对象的实例变量之前一直不能理解。因为类的结构体里存放了一个ivars,一直以为变量的指针也是在类里,但是每一个对象都有自己的实例变量,所以一直不明白这个变量的值到底是怎么存的。

现在我的理解是,类的结构体里存放的都是实例变量的名字,偏移量(编译的时候已经固定好了。)信息。然后我们给实例对象的变量赋值的时候,系统是根据我们的名字,找到对应的ivar,然后通过ivar里的偏移量,计算出来该变量的内存地址(实例对象的内存地址+偏移量),然后进行存取。

分类

struct objc_category {
    char * _Nonnull category_name                            OBJC2_UNAVAILABLE;
    char * _Nonnull class_name                               OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable instance_methods     OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable class_methods        OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

分类是运行时才去加载的。从结构看,它支持添加实例方法,类方法以及属性。
需要注意的是,分类添加的方法会和之前的类的方法放在一起,并且会放在之前的方法的前面。因为运行时查找方法的时候是按照顺序查找的,所以当分类的方法和类的方法重复的时候,会优先使用分类的方法。
分类是可以添加属性的。
我们正常情况下给类生成的属性,编译器会默认做三件事:1.生成一个实例变量 2.生成对应的setter和getter方法。
然而分类是无法添加实例变量的。因为类的内存布局是在编译的时候就完成了,类的结构体里有instance_size,并且每个变量都有对应的偏移地址,这些是在编译后就已经确定的了。系统使用变量的时候是根据这个偏移量来查找的。所以苹果是不允许分类添加变量的。

分类的好处是:1.可以将一些库的私有化方法暴露出来使用。2.分散代码逻辑 3.减少类文件的体积。

method

struct objc_method {
    SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;
    char * _Nullable method_types                            OBJC2_UNAVAILABLE;
    IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

method,iOS开发都是成为方法,和其他语言的函数是一个意思。
不过iOS的method内容还是比较独特的。从结构体看,一个method,包含了三个东西:SEL,Types,IMP。

SEL 方法名(或称为方法编号)
一般可以通过@selector(selector) 和 NSSelectorFromString(@"customClick") 来创建。其实就是类似于函数指针。因为oc不支持函数指针作为参数传递,因此使用SEL包裹一层来传递

IMP 方法的实现 函数指针,指向了方法的地址。

感觉SEL就是类似于key,IMP是value。 objc_msgSend发送消息的时候,通过SEL找到value,去调用。

runtime提供了一些api可以对imp进行操作。比如获取imp,交互两个方法的imp ,设置一个方法的imp。
我们可以利用这些,做到方法替换等等骚操作。

上一篇 下一篇

猜你喜欢

热点阅读