iOS底层探索--OC对象的本质&类的底层结构

序言

  在OC面向对象编程中,我们每天都面对着创建对象,操作对象, 那对象究竟是什么?实例化的对象和类的本质又是什么?

目录

    1. clang查看OC底层的实现
    1. 源码分析
    1. 总结

一、clang查看OC底层的实现

  新建文件main.m,在文件中添加如下代码,添加属性,类方法和实例方法,还有方法的调用。

#import <UIKit/UIKit.h>

@interface PSYPerson : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, assign) BOOL isWorking;

+ (void)run;
- (void)run;

@end

@implementation PSYPerson
+ (void)run
{
    NSLog(@"run class method printf");
}
- (void)run
{
    NSLog(@"run instance method printf");
}

@end


int main(int argc, char * argv[]) {

    @autoreleasepool {
        PSYPerson *psy1 = [[PSYPerson alloc] init];
        [psy1 run];
    }
    return 0;
}

  使用rewrite将其转成C++代码,在终端执行如下命令,即可转成main.cpp文件,由于文件中import了UIKit,所以文件比较大(足有7万多行代码)。

xcrun -sdk iphoneos clang -rewrite-objc -F UIKit -fobjc-arc -arch arm64 main.m

  可直接翻到最后查看自己编写的代码,最后转成了什么。在__DATA, __objc_classlist的section段中,存放着类的列表,具体如下,类是以_class_t的结构体方式存储。

//_class_t的定义
struct _class_t {
    struct _class_t *isa;
    struct _class_t *superclass;
    void *cache;
    void *vtable;
    struct _class_ro_t *ro;
};

static struct _class_t *L_OBJC_LABEL_CLASS_$ [1] __attribute__((used, section ("__DATA, __objc_classlist,regular,no_dead_strip")))= {
    &OBJC_CLASS_$_PSYPerson,
};

上来一些就是元类和类的初始化

extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;
// 元类部分
extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_PSYPerson __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_$_PSYPerson,
};

extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSObject;
// 类部分
extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_PSYPerson __attribute__ ((used, section ("__DATA,__objc_data"))) = {
    0, // &OBJC_METACLASS_$_PSYPerson,
    0, // &OBJC_CLASS_$_NSObject,
    0, // (void *)&_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_CLASS_RO_$_PSYPerson,
};
__declspec(allocate(".objc_inithooks$B")) static void *OBJC_CLASS_SETUP[] = {
    (void *)&OBJC_CLASS_SETUP_$_PSYPerson,
};

其定义如下,可以看到:
元类PSYPerson的isa = 元类NSObject
元类PSYPerson的superclass = 元类NSObject
类PSYPerson的isa = 元类PSYPerson
类PSYPerson的superclass = 类NSObject

static void OBJC_CLASS_SETUP_$_PSYPerson(void ) {
    OBJC_METACLASS_$_PSYPerson.isa = &OBJC_METACLASS_$_NSObject;
    OBJC_METACLASS_$_PSYPerson.superclass = &OBJC_METACLASS_$_NSObject;
    OBJC_METACLASS_$_PSYPerson.cache = &_objc_empty_cache;
    OBJC_CLASS_$_PSYPerson.isa = &OBJC_METACLASS_$_PSYPerson;
    OBJC_CLASS_$_PSYPerson.superclass = &OBJC_CLASS_$_NSObject;
    OBJC_CLASS_$_PSYPerson.cache = &_objc_empty_cache;
}
对象、类、元类的关系图

  类关系图中虚线是isa指针指向流程,实线部分是superclass指针指向流程;类的实例对象(PSYPerson的实例)的isa指向类(PSYPerson类)eg: 线1,类(PSYPerson)的superclass指向它的父类一直到NSObject eg: 线4、线5(NSObject的父类是nil eg:线线6),类(PSYPerson)的isa指向元类(Meta PSYPerson),eg : 线11,元类的superclass指向父元类 eg: 线7,元类的isa指向根元类(Meta NSObject) eg:线12,根元类(Meta NSObject)的isa指向根元类自己(Meta NSObject)eg:线34,根元类(Meta NSObject)的父类是根类,也就是NSObject eg:线35
为了验证上图的关系,首先看一下类方法+ (Class)class和实例方法- (Class)class的源码实现,如下:

+ (Class)class { // class类方法,直接返回类本身
   return self;
}

- (Class)class { // class实例方法,则会获取对象的isa方法
   return object_getClass(self);
}

Class object_getClass(id obj)
{
   if (obj) return obj->getIsa();
   else return Nil;
}

  情景:object 是 NSObject的实例,结合运行时几个函数:object_getClass获取isa,class_getSuperclass获取superclass,class_isMetaClass判断一个类是否是元类对象。

  • 线3可通过 object_getClass(object)验证
  • 线6可通过 class_getSuperclass([object class])验证
  • 线35可通过 class_getSuperclass(object_getClass(NSObject.class))或者class_getSuperclass(object_getClass(object_getClass(object)))验证
  • 线34可通过 object_getClass(object_getClass(NSObject.class))或者object_getClass (object_getClass(object_getClass(object)))验证
验证

  来看类OBJC_CLASS_$_PSYPerson中有一个_OBJC_CLASS_RO_$_PSYPerson,RO(read only)是一个只读属性,在编译时期就确定的。再看一下它的结构体值如下:

static struct _class_ro_t _OBJC_CLASS_RO_$_PSYPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    0, __OFFSETOFIVAR__(struct PSYPerson, _isWorking), sizeof(struct PSYPerson_IMPL), 
    0, 
    "PSYPerson",
    (const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_PSYPerson,
    0, 
    (const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_PSYPerson,
    0, 
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_PSYPerson,
};

  存在MachO的__DATA,__objc_constsection段,其定义如下:

struct _class_ro_t {
    unsigned int flags;
    unsigned int instanceStart;
    unsigned int instanceSize;
    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;
};

  对应的在这个read only的_class_ro_t结构体中,有_method_list_t结构体类型的基方法列表baseMethods

1、实例方法列表
static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count; // 方法个数
    struct _objc_method method_list[9];
} _OBJC_$_INSTANCE_METHODS_PSYPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    9,
    {{(struct objc_selector *)"run", "v16@0:8", (void *)_I_PSYPerson_run},
    {(struct objc_selector *)"name", "@16@0:8", (void *)_I_PSYPerson_name},
    {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_PSYPerson_setName_},
    {(struct objc_selector *)"age", "q16@0:8", (void *)_I_PSYPerson_age},
    {(struct objc_selector *)"setAge:", "v24@0:8q16", (void *)_I_PSYPerson_setAge_},
    {(struct objc_selector *)"nickName", "@16@0:8", (void *)_I_PSYPerson_nickName},
    {(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)_I_PSYPerson_setNickName_},
    {(struct objc_selector *)"isWorking", "B16@0:8", (void *)_I_PSYPerson_isWorking},
    {(struct objc_selector *)"setIsWorking:", "v20@0:8B16", (void *)_I_PSYPerson_setIsWorking_}}
};

_objc_method的定义如下
struct _objc_method {
    struct objc_selector * _cmd;
    const char *method_type;
    void  *_imp;
};

2、类方法列表
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_PSYPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"run", "v16@0:8", (void *)_C_PSYPerson_run}}
};

  有_objc_protocol_list结构体类型的基协议列表baseProtocols

struct protocol_list_t {
    // count is 64-bit by accident. 
    unsigned int protocol_count;
   struct _protocol_t protocol_list[0]; // variable-size
}

协议结构体的声明如下:
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;
};

  有_ivar_list_t结构体类型的成员变量列表ivars

static struct /*_ivar_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count;
    struct _ivar_t ivar_list[4];
} _OBJC_$_INSTANCE_VARIABLES_PSYPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_ivar_t),
    4,
    {{(unsigned long int *)&OBJC_IVAR_$_PSYPerson$_isWorking, "_isWorking", "B", 0, 1},
     {(unsigned long int *)&OBJC_IVAR_$_PSYPerson$_name, "_name", "@\"NSString\"", 3, 8},
     {(unsigned long int *)&OBJC_IVAR_$_PSYPerson$_age, "_age", "q", 3, 8},
     {(unsigned long int *)&OBJC_IVAR_$_PSYPerson$_nickName, "_nickName", "@\"NSString\"", 3, 8}}
};

成员变量结构体_ivar_t的声明如下:
struct _ivar_t {
    unsigned long int *offset;  // pointer to ivar offset location
    const char *name;
    const char *type;
    unsigned int alignment;
    unsigned int  size;
};

  有_prop_list_t结构体类型的属性列表properties

static struct /*_prop_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count_of_properties;
    struct _prop_t prop_list[4];
} _OBJC_$_PROP_LIST_PSYPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    4,
    {{"name","T@\"NSString\",C,N,V_name"},
    {"age","Tq,N,V_age"},
    {"nickName","T@\"NSString\",C,N,V_nickName"},
    {"isWorking","TB,N,V_isWorking"}}
};

属性_prop_t的声明如下:
struct _prop_t {
    const char *name;
    const char *attributes;
};

  感兴趣的读者可以结合MachO可执行文件通过MachOView查看探索,你会得到一个更加清晰的见解。

小结
  1. 实例对象的本质是结构体
  2. 类对象以及元类对象的本质都是结构体
  3. 从main.cpp分析可得到类的结构图
image.png

二、objc源码分析

本文是基于objc4-756.2分析(因为穷Mac硬件只支持到这个版本)

顺着源码一步一步分析可以得到objc_object结构体

struct objc_object { // objc.h
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    ......
    ......
    ......

};
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;

#if SUPPORT_INDEXED_ISA
    uint32_t index;
#endif

};
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    .....
    .....
};

总结

  1. 对象的本质是结构体
  2. class_rw_t是运行时产生的,支持动态添加方法、属性、类等
  3. 所有的对象(实例,类,元类)都存在isa
    • 实例的isa是类
    • 类的isa是元类
    • 元类的isa是Meta NSObject
    • Meta NSObject的isa是自己(Meta NSObject)
    • NSObject的父类是nil
    • Meta NSObject的父类是NSObject
      最后使用一张图总结:
oc类结构程图
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容