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

2021-06-17  本文已影响0人  XingKongMap

前言: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++ 文件

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的呢?

上一篇下一篇

猜你喜欢

热点阅读