iOS类与方法底层实现初探

前言: 学习了OC之后,我们知道类和方法的定义和使用,为了更好理解类和方法,这里我将初步解析iOS类与方法底层实现方式。

首先我们建一个测试类MyTestClass类文件
测试代码如下:

#import <Foundation/Foundation.h>

@interface MyTestClass : NSObject

@property NSString *myTestProperty;
@property (nonatomic, copy) NSString *mySecondProperty;

@end

#import "MyTestClass.h"

@implementation MyTestClass

- (void)myTestMethod {
    NSLog(@"myTestMethod");
}

+ (void)myClassMethod {
    NSLog(@"myClassMethod");
}

然后在终端使用命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc MyTestClass.m,就会将对应iOS代码转成了C++代码.对应文件下就会生成对应的MyTestClass.cpp文件。

现在我们就开始分析这个C++文件:

我们来到MyTestClass.cpp文件最后,可以看到:

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

上面代码是将定义的类写到___DATA,__objc_classlist这个section中。

在从MyTestClass.cpp文件向上看看对类的初始化方法:

//_declspec(dllimport)     是说这个函数是从别的DLL导入。我要用。
//_declspec(dllexport)   是说这个函数要从本DLL导出。我要给别人用。
extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;
extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_MyTestClass __attribute__ ((used, section ("__DATA,__objc_data"))) = {
    0, // &OBJC_METACLASS_$_MyTestClass,
    0, // &OBJC_CLASS_$_NSObject,
    0, // (void *)&_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_CLASS_RO_$_MyTestClass,
};
extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSObject;

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

从上面我们可以看出OBJC_CLASS_$_MyTestClass是一个_class_t结构。
MyTestClass.cpp文件中我们可以找到该结构体的定义:

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

从上面的方法static void OBJC_CLASS_SETUP_$_MyTestClass中可以看到:
1:MyTestClass类的isa指向MyTestClassmetaClass(元类)。
MyTestClasssuperclassNSObject.
MyTestClassmetaClassisa则指向父类NSObjectmetaClass.
根据继承关系,我们可以得到类的关系指向图。这里我们用网上流传很广的一张图:

类关系图

其中图中实线表示superClass指针,虚线表示isa指针。

从图中我们可以看出:
1.类实例的isa指向它的class。类的superClass指向父类,类的isa指向meta-class(元类)。而元类的isa则指向父类的meta-class.
2.直到根元类NSObjectisa指向自己。而他的superClass则是nill.

MyTestClass.cpp文件中我们再找到OBJC_CLASS_$_MyTestClass的相关定义:

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

然后再找到_OBJC_CLASS_RO_$_MyTestClass此变量的定义:

static struct _class_ro_t _OBJC_CLASS_RO_$_MyTestClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    0, __OFFSETOFIVAR__(struct MyTestClass, _myTestProperty), sizeof(struct MyTestClass_IMPL), 
    0, 
    "MyTestClass",
    (const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_MyTestClass,
    0, 
    (const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_MyTestClass,
    0, 
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_MyTestClass,
};

MyTestClass.cpp文件中再找到_class_ro_t结构体的定义

struct _class_ro_t {
    unsigned int flags;//标记
    unsigned int instanceStart;//实例初始化大小
    unsigned int instanceSize;//实例的大小
    const unsigned char *ivarLayout;//记录了哪些是 strong 的 ivar
    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;//记录了哪些是 weak 的 ivar
    const struct _prop_list_t *properties;//@property属性列表
};

_class_ro_t该结构体其实就是objc_class结构体的变形,代表根类的结构体类型。 我们可以在官方公开的runtime源码文件runtime.h,找到objc_class定义:

typedef struct objc_class *Class;
struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

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

从上面我们也可以看出isa指针是和Class同类型的objc_class结构指针。
我们再从MyTestClass.cpp文件中找到_class_ro_t_INSTANCE_METHODS_MyTestClass实例方法的定义:

struct _objc_method {
    struct objc_selector * _cmd;
    const char *method_type;
    void  *_imp;
};
static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[5];
} _OBJC_$_INSTANCE_METHODS_MyTestClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    5,
    {{(struct objc_selector *)"myTestMethod", "v16@0:8", (void *)_I_MyTestClass_myTestMethod},
    {(struct objc_selector *)"myTestProperty", "@16@0:8", (void *)_I_MyTestClass_myTestProperty},
    {(struct objc_selector *)"setMyTestProperty:", "v24@0:8@16", (void *)_I_MyTestClass_setMyTestProperty_},
    {(struct objc_selector *)"mySecondProperty", "@16@0:8", (void *)_I_MyTestClass_mySecondProperty},
    {(struct objc_selector *)"setMySecondProperty:", "v24@0:8@16", (void *)_I_MyTestClass_setMySecondProperty_}}
};

从上面可以看出定义了实例方法的个数和每个方法。方法的定义中包含了方法名,方法签名和方法的实现。其中_myTestMethod_I_MyTestClass_myTestMethod方法具体实现如下:

static void _I_MyTestClass_myTestMethod(MyTestClass * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_nq_7n_dy67x0x13zxzr8d8p9bd00000gn_T_MyTestClass_69c7f3_mi_0);
}

类方法我们要在meta-class(元类)中找。我们从MyTestClass.cpp中找到对应类方法的定义:


static struct _class_ro_t _OBJC_METACLASS_RO_$_MyTestClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    1, sizeof(struct _class_t), sizeof(struct _class_t), 
    0, 
    "MyTestClass",
    (const struct _method_list_t *)&_OBJC_$_CLASS_METHODS_MyTestClass,
    0, 
    0, 
    0, 
    0, 
};
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_MyTestClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"myClassMethod", "v16@0:8", (void *)_C_MyTestClass_myClassMethod}}
};

类对象既然称为对象,那它也是一个实例。类对象中也有一个isa指针指向它的元类(meta class),即类对象是元类的实例。元类内部存放的是类方法列表,根元类的isa指针指向自己,superclass指针指向NSObject类。

最后我们再看下myTestClass的定义:

struct NSObject_IMPL {
    Class isa;
};
struct MyTestClass_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_myTestProperty;
    NSString *_mySecondProperty;
};

小结:我们只是通过一个测试类MyTestClass类文件的C++源码来简单分析了一下其类与方法的实现。

想了解更多可参考:
Objective-C Class Ivar Layout 探索
iOS开发·runtime原理与实践: 基本知识篇(类,超类,元类,super_class,isa,对象,方法,SEL,IMP)

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

推荐阅读更多精彩内容