结构模型

结构模型

  1. 介绍下runtime的内存模型(isa、对象、类、metaclass、结构体的存储信息等)
Class:
typedef struct objc_class *Class;

struct objc_class {

Class isa;  //指向meta_class

Class super_class;     //指向父类

const char* name;

long version;

long info;

long instance_size;

struct objc_ivar_list *ivars; //类的成员变量,使用数组存储,使用中基本不变化(只有动态生成的类才能添加成员变量Ivar),存取效率高。

struct objc_method_list **methodLists;  //类的成员方法,使用链表存储。使用中可能会增加方法,使用链表存储。

struct objc_cache *cache;   //类的方法缓存。

struct objc_protocol_list *protocols;  //类遵守的协议。   

};

对象
struct objc_object {

Class isa; //指向对象所属的类。

};

  1. 为什么要设计metaclass
    元类保存了类方法的列表
  1. class_copyIvarList & class_copyPropertyList区别
    class_copyPropertyList返回的仅仅是对象类的属性(@property申明的属性),而class_copyIvarList返回类的所有属性和变量(包括在@interface大括号中声明的变量)

  2. class_rw_t 和 class_ro_t 的区别
    ObjC 类中的属性、方法还有遵循的协议等信息都保存在 class_rw_t 中:其中还有一个指向常量的指针 ro,其中存储了当前类在编译期就已经确定的属性、方法以及遵循的协议。

  3. category如何被加载的,两个category的load方法的加载顺序,两个
    category的同名方法的加载顺序

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                       OBJC2_UNAVAILABLE;  // 父类
        const char *name                        OBJC2_UNAVAILABLE;  // 类名
        long version                                OBJC2_UNAVAILABLE;  // 类的版本信息,默认0
        long info                                      OBJC2_UNAVAILABLE;  // 类信息,供运行期使用的一些位标识
        long instance_size                      OBJC2_UNAVAILABLE;  // 该类的实例变量大小

        struct objc_ivar_list *ivars           OBJC2_UNAVAILABLE;  // 该类的成员变量链表
        struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  // 方法定义的链表
        struct objc_cache *cache                       OBJC2_UNAVAILABLE;  // 方法缓存
        struct objc_protocol_list *protocols        OBJC2_UNAVAILABLE;  // 协议链表
#endif

} OBJC2_UNAVAILABLE;

struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;    //存储实例方法
    struct method_list_t *classMethods;        //存储类方法
    struct protocol_list_t *protocols;            //协议
    struct property_list_t *instanceProperties;//属性
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};

分类的实现原理是将category中的方法,属性,协议数据放在category_t结构体中,然后将结构体内的方法列表拷贝到类对象的方法列表中。

category的+load执行顺序是根据编译顺序决定的

  1. category & extension区别,能给NSObject添加Extension吗,结果如何
    Category的小括号中有名字,而Extension没有;
    Category只能扩充方法,不能扩充成员变量和属性;
    如果Category声明了声明了一个属性,那么Category只会生成这个属性的set,get方法的声明,也就不是会实现.
  1. 消息转发机制,消息转发机制和其他语言的消息机制优劣对比
    1.category
    2.运行时
    3.消息机制
    4.可以和C,C++,swift混和编程
    1.不支持命名空间
    2.不支持运算符重载
    3.不支持多重继承
    4.使用动态运行时类型,所有的方法都是函数点用,很多编译时的优化方法都用不到,如内联函数

  2. 在方法调用的时候,方法查询-> 动态解析-> 消息转发 之前做了什么
    1、正常判空处理
    2、TAGGED_POINTERS判断(后面文章再一起探究)
    3、通过isa指针拿到他的class(class中存储它的方法以及方法缓存)
    4、CacheLookup 查看方法缓存

  3. IMP、SEL、Method的区别和使用场景
    SEL
    SEL方法选择器,表示一个selector的指针
    无论什么类里,只要方法名相同,SEL就相同。项目里的所有SEL都保存在一个NSSet集合里(NSSet集合里的元素不能重复),所以查找对应方法,只要找到对应的SEL就可以了。
    SEL实际是根据方法名hash化了的字符串
    IMP
    定义:函数指针,指向方法实现的首地址。
    Method
    Method定义如下:它主要是用语描述类里面的方法

typedef struct objc_method *Method;
objc_method结构体定义如下

struct objc_method {
    SEL method_name                              OBJC2_UNAVAILABLE;//方法名
    char *method_types                           OBJC2_UNAVAILABLE;//参数返回值字符串描述
    IMP method_imp                               OBJC2_UNAVAILABLE;//方法的实现
}    
从上述代码可以看出,Method是一个结构体,包含了SEL和IMP成员变量。
实际上,相当于在SEL和IMP之间做了一个映射,有了Method,SEL就可以找到对应的IMP,从而调用方法。
  1. load、initialize方法的区别什么?在继承关系中他们有什么区别
    load和initialize会被自动调用,不能手动调用它们。
    子类实现了load和initialize的话,会隐式调用父类的load和initialize方法
    load和initialize方法内部使用了锁,因此它们是线程安全的。
    2.2 不同点
    子类中没有实现load方法的话,不会调用父类的load方法;而子类如果没有实现initialize方法的话,也会自动调用父类的initialize方法。
    load方法是在类被装在进来的时候就会调用,initialize在第一次给某个类发送消息时调用(比如实例化一个对象),并且只会调用一次,是懒加载模式,如果这个类一直没有使用,就不回调用到initialize方法。
    load
    父类先于子类调用
    类先于分类调用
    initialize 只会在对应类的方法第一次被调用时,如果一个子类没有实现 +initialize 方法,那么父类的实现是会被执行多次的,如果一个类的分类实现了 +initialize 方法,那么就会对这个类中的实现造成覆盖。

推荐阅读更多精彩内容