美文网首页iOS专题
iOS基础(七) - 聊一聊NSObject对象模型

iOS基础(七) - 聊一聊NSObject对象模型

作者: 一剑孤城 | 来源:发表于2017-03-02 22:31 被阅读120次

    前言

    为什么要写NSObject呢?嗯嗯,主要因为手贱。本来,近段时间公司比较闲,然后,鄙人想进阶一下iOS开发,runtime之前看过一些简书,blog等等,但是实际上用的比较少,也不是很清晰,所以就去看runtime的相关资料,然后发现很多不懂的知识,就不断点连接,看看跳跳,然后就来到了NSObject对象模型,索性就先把它弄明白。推荐一篇深入浅出的文章《NSObject对象模型解析(上)》

    1.先看一下结构

    大家可以先看一下NSObject.h头文件

    @interface NSObject <NSObject> {
        Class isa  OBJC_ISA_AVAILABILITY;
    }
    
    - (Class)class;
    

    上面可以看见,NSObject含有isa成员变量,是一个Class类型,继续点进去,到objc.h。

    typedef struct objc_class *Class;
    

    Class是一个结构体指针,现在我们知道了isa是一个objc_class结构体指针。继续点objc_class进去,到runtime.h。

    struct objc_class {
        Class isa  OBJC_ISA_AVAILABILITY;
    
    #if !__OBJC2__
        Class super_class                                        OBJC2_UNAVAILABLE;
        const char *name                                         OBJC2_UNAVAILABLE;
        long version                                             OBJC2_UNAVAILABLE;
        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;
    

    激动人心的时刻,终于看到了这个经典的结构体objc_class。现在,我们来分析一下:

    isa    //指向本类meta-class的结构体指针,meta-class存在的意义在于,调用类方法和调用实例方法是同一套消息传递机制。
    super_class    //看名字就知道指向本类的结构体指针
    name    //类名
    version    //类版本信息
    info    //类相关的一些信息
    instance_size    //类所有实例变量的大小
    ivars    //类的成员变量列表
    methodLists    //函数列表
    cache    //缓存使用过的方法列表,提高访问速度
    protocols    //协议列表
    

    类的结构大概就是这样的东西了,来实际编译一下看源代码

    新一个类:ClassA
    ClassA.h
    @protocol ClassAProtocol <NSObject>
    
    - (void)protocolMethod;
    
    @end
    
    @interface ClassA : NSObject<ClassAProtocol>
    
    @property (nonatomic, strong) NSString *publicStr1;
    @property (nonatomic, copy) NSString *publicStr2;
    
    + (void)classMethod;
    - (void)publicMethod;
    
    @end
    
    ClassA.m
    @interface ClassA ()
    
    @property (nonatomic, strong) NSString *privateStr1;
    @property (nonatomic, copy) NSString *privateStr2;
    
    @end
    
    @implementation ClassA {
        NSString *privateStr3;
    }
    
    #pragma mark - Class method
    + (void)classMethod {}
    
    #pragma mark - Public
    - (void)publicMethod {}
    
    #pragma mark - Private
    - (void)privateMethod {}
    
    #pragma mark - ClassAProtocol
    - (void)protocolMethod {}
    
    @end
    

    打开终端,进入ClassA.m文件所在目录,执行下面命令

    clang -rewrite-objc ClassA.m
    

    打开生成的ClassA.cpp文件,定位到最底端,我们来一一分析。

    找到ClassA.h声明头文件
    #ifndef _REWRITER_typedef_ClassA
    #define _REWRITER_typedef_ClassA
    typedef struct objc_object ClassA;
    typedef struct {} _objc_exc_ClassA;
    #endif
    
    extern "C" unsigned long OBJC_IVAR_$_ClassA$_publicStr1;
    extern "C" unsigned long OBJC_IVAR_$_ClassA$_publicStr2;
    extern "C" unsigned long OBJC_IVAR_$_ClassA$_privateStr1;
    extern "C" unsigned long OBJC_IVAR_$_ClassA$_privateStr2;
    struct ClassA_IMPL {
        struct NSObject_IMPL NSObject_IVARS;
        NSString *privateStr3;
        NSString *_publicStr1;
        NSString *_publicStr2;
        NSString *_privateStr1;
        NSString *_privateStr2;
    };
    

    上面可以看到我们定义的属性和成员变量,我们再往下看:

    static void _C_ClassA_classMethod(Class self, SEL _cmd) {}
    
    
    
    static void _I_ClassA_publicMethod(ClassA * self, SEL _cmd) {}
    
    
    
    static void _I_ClassA_privateMethod(ClassA * self, SEL _cmd) {}
    
    
    
    static void _I_ClassA_protocolMethod(ClassA * self, SEL _cmd) {}
    

    找到类方法,公有实例方法和私有实例方法,继续往下看。

    struct _objc_method {
        struct objc_selector * _cmd;
        const char *method_type;
        void  *_imp;
    };
    
    struct _protocol_t {
        void * isa;  // NULL
        const char *protocol_name;
        const struct _protocol_list_t * protocol_list; // super protocols
        const struct method_list_t *instance_methods;
        const struct method_list_t *class_methods;
        const struct method_list_t *optionalInstanceMethods;
        const struct method_list_t *optionalClassMethods;
        const struct _prop_list_t * properties;
        const unsigned int size;  // sizeof(struct _protocol_t)
        const unsigned int flags;  // = 0
        const char ** extendedMethodTypes;
    };
    
    struct _ivar_t {
        unsigned long int *offset;  // pointer to ivar offset location
        const char *name;
        const char *type;
        unsigned int alignment;
        unsigned int  size;
    };
    
    struct _class_ro_t {
        unsigned int flags;
        unsigned int instanceStart;
        unsigned int instanceSize;
        unsigned int reserved;
        const unsigned char *ivarLayout;
        const char *name;
        const struct _method_list_t *baseMethods;
        const struct _objc_protocol_list *baseProtocols;
        const struct _ivar_list_t *ivars;
        const unsigned char *weakIvarLayout;
        const struct _prop_list_t *properties;
    };
    
    struct _class_t {
        struct _class_t *isa;
        struct _class_t *superclass;
        void *cache;
        void *vtable;
        struct _class_ro_t *ro;
    };
    
    struct _category_t {
        const char *name;
        struct _class_t *cls;
        const struct _method_list_t *instance_methods;
        const struct _method_list_t *class_methods;
        const struct _protocol_list_t *protocols;
        const struct _prop_list_t *properties;
    };
    

    上面可以看到method,protocol,ivar以及class结构体定义,重点放在_class_ro_t这个结构体,是不是和runtime里面定义的objc_class结构体有点像。再看一下_class_t这个结构体,是不是除了包含了isa指针和superclass指针还包含_class_ro_t这个结构体,答案呼之欲出了,_class_t结构体就是objc_class编译成c的结构。下面继续看一下成员变量和属性变成什么样子。

    static struct /*_ivar_list_t*/ {
        unsigned int entsize;  // sizeof(struct _prop_t)
        unsigned int count;
        struct _ivar_t ivar_list[5];
    } _OBJC_$_INSTANCE_VARIABLES_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
        sizeof(_ivar_t),
        5,
        {{(unsigned long int *)&OBJC_IVAR_$_ClassA$privateStr3, "privateStr3", "@\"NSString\"", 3, 8},
         {(unsigned long int *)&OBJC_IVAR_$_ClassA$_publicStr1, "_publicStr1", "@\"NSString\"", 3, 8},
         {(unsigned long int *)&OBJC_IVAR_$_ClassA$_publicStr2, "_publicStr2", "@\"NSString\"", 3, 8},
         {(unsigned long int *)&OBJC_IVAR_$_ClassA$_privateStr1, "_privateStr1", "@\"NSString\"", 3, 8},
         {(unsigned long int *)&OBJC_IVAR_$_ClassA$_privateStr2, "_privateStr2", "@\"NSString\"", 3, 8}}
    };
    

    如上所示,成员变量都变成了类似OBJC_IVAR_$_ClassA$privateStr3这样的结构,很容易就能看出来。看一下方法列表:

    static struct /*_method_list_t*/ {
        unsigned int entsize;  // sizeof(struct _objc_method)
        unsigned int method_count;
        struct _objc_method method_list[11];
    } _OBJC_$_INSTANCE_METHODS_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
        sizeof(_objc_method),
        11,
        {{(struct objc_selector *)"publicMethod", "v16@0:8", (void *)_I_ClassA_publicMethod},
        {(struct objc_selector *)"privateMethod", "v16@0:8", (void *)_I_ClassA_privateMethod},
        {(struct objc_selector *)"protocolMethod", "v16@0:8", (void *)_I_ClassA_protocolMethod},
        {(struct objc_selector *)"publicStr1", "@16@0:8", (void *)_I_ClassA_publicStr1},
        {(struct objc_selector *)"setPublicStr1:", "v24@0:8@16", (void *)_I_ClassA_setPublicStr1_},
        {(struct objc_selector *)"publicStr2", "@16@0:8", (void *)_I_ClassA_publicStr2},
        {(struct objc_selector *)"setPublicStr2:", "v24@0:8@16", (void *)_I_ClassA_setPublicStr2_},
        {(struct objc_selector *)"privateStr1", "@16@0:8", (void *)_I_ClassA_privateStr1},
        {(struct objc_selector *)"setPrivateStr1:", "v24@0:8@16", (void *)_I_ClassA_setPrivateStr1_},
        {(struct objc_selector *)"privateStr2", "@16@0:8", (void *)_I_ClassA_privateStr2},
        {(struct objc_selector *)"setPrivateStr2:", "v24@0:8@16", (void *)_I_ClassA_setPrivateStr2_}}
    };
    

    实例方法都变成了objc_selector的结构体指针,还包含系统默认自定义的get和set的方法,但是仔细一看,貌似方法列表少了类方法。为什么呢?大家看一下这个结构体的命名_OBJC$_INSTANCE_METHODS_ClassA,是不是想到了什么?是的,这个只是实例方法,类方法继续往下看:

    static struct /*_method_list_t*/ {
        unsigned int entsize;  // sizeof(struct _objc_method)
        unsigned int method_count;
        struct _objc_method method_list[1];
    } _OBJC_$_CLASS_METHODS_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
        sizeof(_objc_method),
        1,
        {{(struct objc_selector *)"classMethod", "v16@0:8", (void *)_C_ClassA_classMethod}}
    };
    

    _OBJC$_CLASS_METHODS_ClassA,这个命名够明显了吧,类方法列表。等等,我们是不是忘记了什么?有人会说,协议呢?协议方法呢?别急,下面给你呈现出来。

    static struct /*_method_list_t*/ {
        unsigned int entsize;  // sizeof(struct _objc_method)
        unsigned int method_count;
        struct _objc_method method_list[1];
    } _OBJC_PROTOCOL_INSTANCE_METHODS_ClassAProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
        sizeof(_objc_method),
        1,
        {{(struct objc_selector *)"protocolMethod", "v16@0:8", 0}}
    };
    
    struct _protocol_t _OBJC_PROTOCOL_ClassAProtocol __attribute__ ((used)) = {
        0,
        "ClassAProtocol",
        (const struct _protocol_list_t *)&_OBJC_PROTOCOL_REFS_ClassAProtocol,
        (const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_ClassAProtocol,
        0,
        0,
        0,
        0,
        sizeof(_protocol_t),
        0,
        (const char **)&_OBJC_PROTOCOL_METHOD_TYPES_ClassAProtocol
    };
    

    是不是看见自己定义的协议和协议方法了。嘿嘿嘿,客官可真是慧眼呀😄。再往下看。

    static struct /*_prop_list_t*/ {
        unsigned int entsize;  // sizeof(struct _prop_t)
        unsigned int count_of_properties;
        struct _prop_t prop_list[2];
    } _OBJC_$_PROP_LIST_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
        sizeof(_prop_t),
        2,
        {{"publicStr1","T@\"NSString\",&,N,V_publicStr1"},
        {"publicStr2","T@\"NSString\",C,N,V_publicStr2"}}
    };
    

    显而易见,上面就是属性列表。我们之前ClassA定义的属性,成员变量,方法,协议都能找到相应的实现。

    2.解析一下类(Class)与元类(meta-class)的关系

    先上一张大家很熟悉的图:


    NSObject.png

    先说一下,一个类的isa指针,meta-class以及superclass怎么获取,代码呈上:

    Class object_getClass(id obj)    //获取isa指针
    objc_getMetaClass(const char *name)    //获取meta-class
    objc_getClass(const char *name)    //获取本类
    class_getSuperclass(Class cls)    //获取父类
    

    下面解析一下这张图怎么得到的:

    新建两个类:ClassA继承NSObject,ClassB继承ClassA
    @interface ClassA : NSObject
    
    @end
    
    @interface ClassB : ClassA
    
    @end
    

    同样,clang一下,编译成.cpp文件,我们再来逐步分析。
    先看ClassA:

    static void OBJC_CLASS_SETUP_$_ClassA(void ) {
        OBJC_METACLASS_$_ClassA.isa = &OBJC_METACLASS_$_NSObject;
        OBJC_METACLASS_$_ClassA.superclass = &OBJC_METACLASS_$_NSObject;
        OBJC_METACLASS_$_ClassA.cache = &_objc_empty_cache;
        OBJC_CLASS_$_ClassA.isa = &OBJC_METACLASS_$_ClassA;
        OBJC_CLASS_$_ClassA.superclass = &OBJC_CLASS_$_NSObject;
        OBJC_CLASS_$_ClassA.cache = &_objc_empty_cache;
    }
    

    从上面的代码我们可以看出来:

    ClassA.isa->ClassA_Meta_Class
    ClassA.superclass->NSObject_Class
    ClassA_Meta_Class.isa->NSObject_Meta_Class
    ClassA_Meta_Class.superclass->NSObject_Meta_Class
    

    我们再看ClassB:

    static void OBJC_CLASS_SETUP_$_ClassB(void ) {
        OBJC_METACLASS_$_ClassB.isa = &OBJC_METACLASS_$_NSObject;
        OBJC_METACLASS_$_ClassB.superclass = &OBJC_METACLASS_$_ClassA;
        OBJC_METACLASS_$_ClassB.cache = &_objc_empty_cache;
        OBJC_CLASS_$_ClassB.isa = &OBJC_METACLASS_$_ClassB;
        OBJC_CLASS_$_ClassB.superclass = &OBJC_CLASS_$_ClassA;
        OBJC_CLASS_$_ClassB.cache = &_objc_empty_cache;
    }
    

    同样,我们可以得到:

    ClassB.isa->ClassB_Meta_Class
    ClassB.superclass->ClassA
    ClassB_Meta_Class.isa->NSObject_Meta_Class
    ClassB_Meta_Class.superclass->ClassA_Meta_Class
    

    结合ClassA和ClassB我们可以得出下图:


    NSObject1.png

    是不是觉得和上面的图还是有点不一样,不着急,现在我们通过代码来补全这张图,顺便验证一下刚才画的逻辑图的正确性。GoGoGo!

    ClassB *classB = [ClassB new];
    Class myClass = [classB class];
    while (myClass != nil) {
        Class superClass = class_getSuperclass(myClass);
        Class metaClass = object_getClass(myClass);
        Class superMetaClass = class_getSuperclass(metaClass);
        Class isaMetaClass = object_getClass(metaClass);
        const char *myClassName = class_getName(myClass);
        NSString *className = [NSString stringWithUTF8String: myClassName];
        NSLog(@"%@: %p\n%@_superClass: %p\n%@_meta_class: %p\n%@_meta_superclass: %p\n%@_isa_meta_class: %p\n", className, myClass, className, superClass, className, metaClass, className, superMetaClass, className, isaMetaClass);
        myClass = superClass;
    }
    
    //输出
    2017-03-02 22:05:49.520227 ObjCRuntimeDemo[9490:695334] ClassB: 0x100003b38
    ClassB_superClass: 0x100003bd8
    ClassB_meta_class: 0x100003b10
    ClassB_meta_superclass: 0x100003bb0
    ClassB_isa_meta_class: 0x7fffaec320f0
    2017-03-02 22:05:49.520509 ObjCRuntimeDemo[9490:695334] ClassA: 0x100003bd8
    ClassA_superClass: 0x7fffaec32140
    ClassA_meta_class: 0x100003bb0
    ClassA_meta_superclass: 0x7fffaec320f0
    ClassA_isa_meta_class: 0x7fffaec320f0
    2017-03-02 22:05:49.520570 ObjCRuntimeDemo[9490:695334] NSObject: 0x7fffaec32140
    NSObject_superClass: 0x0
    NSObject_meta_class: 0x7fffaec320f0
    NSObject_meta_superclass: 0x7fffaec32140
    NSObject_isa_meta_class: 0x7fffaec320f0
    Program ended with exit code: 0
    
    //每个类需要打印5个地址,分别是本类的地址,本类继承的父类地址,本类元类地址,本类元类父类地址,本类元类isa指针指向的地址
    

    由上述代码以及输出,我们验证了第一张图的正确性,并且进一步完善了第一张图,如下:


    NSObject2.png

    看上去是不是和最开始的逻辑图很像,再加上实例对象的isa指针的指向,替换一下子类,父类和根类的概念,完全就一样了。所以说,类和元类之前的关系,就如上图,每个类都有特定的元类,类的isa指针指向元类,元类拥有着类一样的结构,并且每个元类的isa指针都是指向根元类,而元类的出现就是为了让类方法的调用和实例方法保持一致。下面看类和元类编译的c代码:

    extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;
    
    extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_ClassA __attribute__ ((used, section ("__DATA,__objc_data"))) = {
        0, // &OBJC_METACLASS_$_NSObject,
        0, // &OBJC_METACLASS_$_NSObject,
        0, // (void *)&_objc_empty_cache,
        0, // unused, was (void *)&_objc_empty_vtable,
        &_OBJC_METACLASS_RO_$_ClassA,
    };
    
    extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSObject;
    
    extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_ClassA __attribute__ ((used, section ("__DATA,__objc_data"))) = {
        0, // &OBJC_METACLASS_$_ClassA,
        0, // &OBJC_CLASS_$_NSObject,
        0, // (void *)&_objc_empty_cache,
        0, // unused, was (void *)&_objc_empty_vtable,
        &_OBJC_CLASS_RO_$_ClassA,
    };
    //meta_class和class类型都是_class_t结构体
    

    3.总结

    上面说了一大堆,废话也不少,第一次这么卖力写文章,说起来都有点感动😹。其实,主要就是NSObject对象模型的解析,以及类和元类的关系,大家有兴趣的自己去编译一下,肯定会有收获的。

    参考:

    NSObject对象模型解析(上)
    iOS:运行时消息传递
    Objective-C Runtime

    相关文章

      网友评论

        本文标题:iOS基础(七) - 聊一聊NSObject对象模型

        本文链接:https://www.haomeiwen.com/subject/tklkgttx.html