1、元类

  • 我们都知道对象的isa是指向类,类其实也是一个对象,可以称为类对象,其isa的位域指向苹果定义的元类
  • 元类是系统给的,其定义和创建都是由编译器完成,在这个过程中,类的归属来自于元类
  • 元类是类对象的类,每个类都有一个独一无二的元类用来存储类方法的相关信息。
  • 元类本身是没有名称的,由于与类相关联,所以使用了同类名一样的名称

关系链:对象 --> 类 --> 元类 --> NSobject,NSObject指向自身

2、NSObject到底有几个?

最后NSObject类的元类也是NSObject,内存中只存在一份根元类NSObject,根元类的元类是指向它自己

[面试题]:类存在几份?

由于类的信息在内存中永远只存在一份,所以类对象只有一份

3、著名的isa走位和继承关系图

image.png
isa走位
  • 实例对象(Instance of Subclass)的isa指向类(class)
  • 类对象(class)isa指向元类(Meta class)
  • 元类(Meta class)的isa指向根元类(Root metal class)
  • 根元类(Root metal class)的isa指向它自己本身,形成闭环,这里的根元类就是NSObject
superclass走位(即继承关系)

类之间的继承关系:

  • 类(subClass)继承自父类(superClass)
  • 父类(superClass)继承自根类(RootClass),此时的根类是指NSObject
  • 根类继承自nil,所以根类即NSObject可以理解为万物起源,即无中生有

元类也存在继承,元类之间的继承关系如下:

  • 子类的元类(metal SubClass)继承自父类的元类(metal SuperClass)
  • 父类的元类(metal SuperClass)继承自根元类(Root metal Class
  • 根元类(Root metal Class)继承于根类(Root class),此时的根类是指NSObject

注意:实例对象之间没有继承关系,类之间有继承关系

4、objc_class & objc_object

为什么对象和类都有isa属性呢?这里就不得不提到两个结构体类型:objc_class & objc_object

NSObject的底层编译是NSObject_IMPL结构体,其中 Class是isa指针的类型,是由objc_class定义的类型,而objc_class是一个结构体。在iOS中,所有的Class都是以 objc_class 为模板创建的

struct NSObject_IMPL {
    Class isa;
};


typedef struct objc_class *Class;
struct objc_object {
    Class _Nonnull isa __attribute__((deprecated));
};
objc_class 与 objc_object 有什么关系?
  • 结构体类型objc_class继承自objc_object类型,其中objc_object也是一个结构体,且有一个isa属性,所以objc_class也拥有了isa属性
  • mian.cpp底层编译文件中,NSObject中的isa在底层是由Class定义的,其中class的底层编码来自objc_class类型,所以NSObject也拥有了isa属性
  • NSObject是一个类,用它初始化一个实例对象objc,objc满足objc_object的特性(即有isa属性),主要是因为isa是由NSObject从objc_class继承过来的,而objc_class继承自objc_object,objc_object有isa属性。所以对象都有一个isa,isa表示指向,来自于当前的objc_object
  • objc_object(结构体)是当前的根对象,所有的对象都有这样一个特性objc_object,即拥有isa属性
objc_object 与对象的关系
  • 所有的对象都是以objc_object为模板继承过来的
  • 所有的对象是来自NSObject(OC),但是真正到底层的是一个objc_object(C/C++)的结构体类型

5、方法列表存储位置

  • 类的实例方法存储在类的bits属性中,通过bits --> methods() --> list获取实例方法列表,例如CJLPersong类的实例方法sayHello就存储在CJLPerson类的bits属性中,类中的方法列表除了包括实例方法,还包括属性的set方法和get方法
  • 类的类方法存储在元类的bits属性中,通过元类bits --> methods() --> list获取类方法列表,例如CJLPerson中的类方法sayBye就存储在CJLPerson类的元类(名称也是CJLPerson)的bits属性中

6、什么是属性、成员变量、实例变量?

  • 属性(property):在OC中是通过@property开头定义,且是带下划线成员变量 + setter + getter方法的变量
  • 成员变量(ivar):在OC的类中{}中定义的,且没有下划线的变量
  • 实例变量:通过当前对象类型,具备实例化的变量,是一种特殊的成员变量,例如NSObject、UILabel、UIButton等

7、元类中为什么会有类对象的类方法?

lgObjc_copyMethodList函数分析

在这个函数中,主要是获取类中的方法列表,实例方法存储在类中,类方法存储在元类中

lgInstanceMethod_classToMetaclass函数分析

在分析前,需要先了解class_getInstanceMethod这个方法,主要是用于获取实例方法,如果在传入的类或者类的父类中没有找到指定的实例方法,则返回NULL

lgClassMethod_classToMetaclass函数分析

在分析前,需要先了解class_getClassMethod这个方法,主要是用于获取类方法,如果在传入的类或者类的父类中没有找到指定的类方法,则返回NULL

//获取类方法
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

//获取元类
 // NOT identical to this->ISA when this is a metaclass 判断是否是元类,是元类就直接返回,反之,继续找isa指向
Class getMeta() {
    if (isMetaClass()) return (Class)this;
    else return this->ISA();
}

看该方法的源码实现,可以得出class_getClassMethod的实现是获取类的类方法,其本质就是获取元类的实例方法,最终还是会走到class_getInstanceMethod,但是在这里需要注意的一点是:在getMeta源码中,如果判断出cls是元类,那么就不会再继续往下递归查找,会直接返回this,其目的是为了防止元类的无限递归查找

源码流程图如下所示

image.png
lgIMP_classToMetaclass函数分析

class_getMethodImplementation 主要是返回方法的具体实现

IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

    if (!cls  ||  !sel) return nil;

    //查找方法实现
    imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);

    //如果没有找到,则进行消息转发
    if (!imp) {
        return _objc_msgForward;
    }

    return imp;
}

该函数在向类实例发送消息时会被调用,并返回一个指向方法实现函数的指针。这个函数会比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函数指针可能是一个指向runtime内部的函数,而不一定是方法的实际实现。如果类实例无法响应selector,则返回的函数指针将是运行时消息转发机制的一部分

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

推荐阅读更多精彩内容

  • 【日精进打卡第1437天】 姓名:陈权 公司:青柠养车 【知~学习】 《流利说》打卡第366天 《羊皮卷》 1、诵...
    水青柠阅读 141评论 0 0
  • 1、近两年忘性很厉害,比如正在厨房,要去卧室拿东西急用,走到卧室却怎么也想不起来要干什么,从卧室里出来又想不起来我...
    君君_4e0c阅读 158评论 0 3
  • 夜莺2517阅读 127,712评论 1 9
  • 版本:ios 1.2.1 亮点: 1.app角标可以实时更新天气温度或选择空气质量,建议处女座就不要选了,不然老想...
    我就是沉沉阅读 6,878评论 1 6
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,523评论 28 53