美文网首页
探索Objective-C中的class和对象在C++中的原理实

探索Objective-C中的class和对象在C++中的原理实

作者: XingKongMap | 来源:发表于2021-06-17 11:22 被阅读0次

    前言:OC 可以编译成C++,从C++的角度探索class和对象的的本质

    1、把OC文件转成C++文件

    生成下面的.m文件,_2415做为一个标识符

    #import <Foundation/Foundation.h>
    @interface Persion_2415 : NSObject
    @property(nonatomic, copy) NSString * name_2415;
    @property(nonatomic, assign) int age_2415;
    -(int) doWork_2415:(int) week;
    @end
    @implementation Persion_2415
    -(int) age_2415{
        return self.age_2415;
    }
    -(int) doWork_2415:(int) week {
        return 39990;
    }
    +(int) clDoWork_2415:(int) week {
        return 500000;
    }
    @end
    
    
    @interface Student_2415 : Persion_2415
    @property(nonatomic, copy) NSString * school_2415;
    @end
    
    @implementation Student_2415
    @end
    
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            Persion_2415 * persion_2415 = [[Persion_2415 alloc] init];
            
            NSLog(@"%@",persion_2415);
        }
        return 0;
    }
    
    
    

    把OC文件生成C++ 文件

    • clang -rewrite-objc main.m -o main.cpp

    2、探索main.cpp

    在main.cpp中查找Persion_2415

    #ifndef _REWRITER_typedef_Persion_2415
    #define _REWRITER_typedef_Persion_2415
    typedef struct objc_object Persion_2415;   //发现Persion_2415是一个struct objc_object结构
    typedef struct {} _objc_exc_Persion_2415;
    #endif
    
    extern "C" unsigned long OBJC_IVAR_$_Persion_2415$_name_2415;
    extern "C" unsigned long OBJC_IVAR_$_Persion_2415$_age_2415;
    struct Persion_2415_IMPL {                 //存储Persion的数据是一个struct Persion_2415_IMPL的数据结构
        struct NSObject_IMPL NSObject_IVARS;
        int _age_2415;
        NSString *_name_2415;
    };
    
    

    再看看struct objc_object的定义,搜索struct objc_object {,发现代码

    typedef struct objc_class *Class;     //Class是struct objc_class * 类型
    
    struct objc_object {                  //struct objc_object定义
        Class _Nonnull isa __attribute__((deprecated));
    };                                                                      
    
    typedef struct objc_object *id;       // id的定义
    
    typedef struct objc_selector *SEL;    //SEL的定义
    

    这时候,发现了很多东西,id,SEL,Class都是结构指针,对象也是,如persion对象在OC中的写法就是Persion * persion = struct objc_object * persion,和id的定义是一样的,所以id可以代表所有对象

    这时候搜索struct objc_class { 发现搜索不到struct objc_class的现实,这时在苹果提供的runtime源码(看别人介绍的)中搜索发现

    struct objc_class {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;                                                                      //isa
    
    #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;
    

    同样在runtime源码中搜索struct objc_selector {,发现搜索不到,暂停struct objc_selector的探索

    这里发现一个疑问既然对象是一个struct objc_object 结构的指针,那么怎么通过struct objc_object 指针来找到struct Persion_2415_IMPL 这里真正存数据的结构呢,暂时无法得知,停止探索

    搜索一下doWork_2415 这个方法,发现:

    static int _I_Persion_2415_doWork_2415_(Persion_2415 * self, SEL _cmd, int week) {
        return 39990;
    }
    static int _C_Persion_2415_clDoWork_2415_(Class self, SEL _cmd, int week) {
        return 500000;
    }
    

    原来每个类的对象方法和类方法都是一个静态的函数

    还发现了:

    static struct /*_method_list_t*/ {
        unsigned int entsize;  // sizeof(struct _objc_method)
        unsigned int method_count;
        struct _objc_method method_list[8];
    } _OBJC_$_INSTANCE_METHODS_Persion_2415 __attribute__ ((used, section ("__DATA,__objc_const"))) = {
        sizeof(_objc_method),
        8,
        {{(struct objc_selector *)"age_2415", "i16@0:8", (void *)_I_Persion_2415_age_2415},
        {(struct objc_selector *)"doWork_2415:", "i20@0:8i16", (void *)_I_Persion_2415_doWork_2415_},
        {(struct objc_selector *)"name_2415", "@16@0:8", (void *)_I_Persion_2415_name_2415},
        {(struct objc_selector *)"setName_2415:", "v24@0:8@16", (void *)_I_Persion_2415_setName_2415_},
        {(struct objc_selector *)"setAge_2415:", "v20@0:8i16", (void *)_I_Persion_2415_setAge_2415_},
        {(struct objc_selector *)"name_2415", "@16@0:8", (void *)_I_Persion_2415_name_2415},
        {(struct objc_selector *)"setName_2415:", "v24@0:8@16", (void *)_I_Persion_2415_setName_2415_},
        {(struct objc_selector *)"setAge_2415:", "v20@0:8i16", (void *)_I_Persion_2415_setAge_2415_}}
    };
    

    这时候有个新的结构 struct _objc_method,搜索下struct _objc_method {,发现了定义

    struct _objc_method {
        struct objc_selector * _cmd;
        const char *method_type;
        void  *_imp;
    };
    

    这里有个疑问method_type的规则是什么呢,百度一下发现了一张表,但是字母后面的基地址偏移数字计算方法不清楚,但是如果需要使用到的话可以使用method_getTypeEncoding方法测试获取

    贴张表(引用自网站https://blog.csdn.net/mr_yong/article/details/50379240

    image-20210617102659440.png
    image-20210617110516731.png

    搜索下OBJC$_INSTANCE_METHODS_Persion_2415,查看在哪里被引用了,发现:

    static struct _class_ro_t _OBJC_CLASS_RO_$_Persion_2415 __attribute__ ((used, section ("__DATA,__objc_const"))) = {
        0, __OFFSETOFIVAR__(struct Persion_2415, _age_2415), sizeof(struct Persion_2415_IMPL), 
        (unsigned int)0, 
        0, 
        "Persion_2415",
        (const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_Persion_2415,
        0, 
        (const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_Persion_2415,
        0, 
        (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Persion_2415,
    };
    

    搜索 struct _class_ro_t {,发现:

    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 _ivar_t {, struct _protocol_t { ,发现:

    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;
    };
    

    搜索OBJC_CLASS_RO$_Persion_2415 在哪里被使用了发现:

    extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_Persion_2415 __attribute__ ((used, section ("__DATA,__objc_data"))) = {
        0, // &OBJC_METACLASS_$_Persion_2415,
        0, // &OBJC_CLASS_$_NSObject,
        0, // (void *)&_objc_empty_cache,
        0, // unused, was (void *)&_objc_empty_vtable,
        &_OBJC_CLASS_RO_$_Persion_2415,
    };
    

    搜索struct _class_t { 在哪里发现:

    struct _class_t {
        struct _class_t *isa;
        struct _class_t *superclass;
        void *cache;
        void *vtable;
        struct _class_ro_t *ro;
    };
    

    到这里class就比较清晰了,再看下main函数,可以发现方法的调用都是通过objc_msgSend 这个函数的

    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
    
            Persion_2415 * persion_2415 = ((Persion_2415 *(*)(id, SEL))(void *)objc_msgSend)((id)((Persion_2415 *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Persion_2415"), sel_registerName("alloc")), sel_registerName("init"));
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_2k_hpcfw7594615f89pv2ydfbmh__2sjv_T_main_c2be56_mi_0,persion_2415);
        }
        return 0;
    }
    

    总结:

    1. objc对象和id类型在C++中都是struct objc_object * 结构体指针

    2. 类的成员方法和类方法的实现都是static 函数,有两个默认参数self, SEL _cmd

    3. 类方法和对象方法的调用都是通过 objc_msgSend 来调用的

    疑问:

    objc对象 是struct objc_object *的指针,但是struct objc_object 结构中只保存的类的信息,对象的信息存储是在Persion_2415_IMPL里的,那么是怎么关联到Persion_2415_IMPL的呢?

    相关文章

      网友评论

          本文标题:探索Objective-C中的class和对象在C++中的原理实

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