tingxins

Creep before you walk.

嘿, 我叫李昕(@tingxins), 一名 iOS 开发者, 热衷移动, 喜爱前端, 正在探索编程艺术之旅。


我的开源项目

Objective-C 中的对象、类、元类

instance-class-meta_class-bg

前言

现在写文章拖延症特别严重啊 (😂)……

本文我们将复习一下 Objective-C 中的一些关于类的知识。

在开发过程中,类与对象相信大家再熟悉不过了,有时我们也会接触一个比较陌生的概念,元类(metaclass),甚至在回头来想时,发现类与对象是什么都开始犯糊涂了,本文主要探讨这三者之间的关系以及在消息转发中各自扮演的角色,希望读者看完本文能有所收获。如有不妥的地方还望大家及时帮忙纠正。

什么是类?

在面向对象编程语言中,是一个非常重要的概念,理解了它,能更好的造轮子、能更好的面向对象编程、能写出模块化的代码、更能提高代码的可读性及后期维护性。

是数据及行为的封装体,在 Objective-C 中,在数据上,定义了内存分配大小、内存布局以及成员变量数据类型等,在行为上,定义了实例方法等。我们可以简单的把比作为某个产品的设计稿。

通过查阅 Apple 官方开源的 objc 源码(官方最新版—>传送门),得知类的数据结构(其中字段本文不做解释,可自行 Google),如下:


typedef struct objc_class *Class;

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    // formerly cache pointer and vtable
    cache_t cache;  
    // class_rw_t * plus custom rr/alloc flags              
    class_data_bits_t bits;    

    class_rw_t *data() { 
        return bits.data();
    }
    .
    .
    .
    ...(省略)
}
    

看完这小段代码,细心的同学会发现,objc_class 继承自 objc_object,没错,类其实也是一个对象,既然类是一个对象,那么它一定是某个类的实例。先不讨论此问题,我们先来谈谈对象。

什么是对象?

对象一定是某个具体的一个实例,可以简单理解为的“值”,可直接使用,就像洗衣机(对象)一样可以直接用来洗衣服(数据),但洗衣机的设计稿()却不能。对象具有动态性,它有自己的生命周期,对象在生命周期结束时会调用 dealloc 方法。

在 Objective-C 中,含有一个 isa 指针并且可以正确指向某个类的数据结构,都可以视作为一个对象,其中 isa 指针指向当前对象所属的类。通过查阅 Apple 官方开源的 objc 源码(官方最新版—>传送门),得知类的数据结构(其中字段本文不做解释,可自行 Google),如下:


struct objc_object {
private:
    isa_t isa;
    .
    .
    .
    ...(省略)
}

每当要向某个对象发送一个消息时,都会通过 isa 指针找到该对象所属的类(因为类定义了对象的行为),然后再遍历其方法缓存表或者方法列表(行为),通过 SEL 找到后取出方法(Method)中的 IMP 函数入口指针,并执行该函数,如果找不到该方法,则进入消息转发等等,此处本文不做概述。

讨论完对象之后,我们再来回顾上文遗留的一个问题:类既然是一个对象,那么这个对象的类是什么呢? 接下来我们将讨论此问题。

什么是元类?

元类是类对象的类。简单的说类描述的是对象,那么元类描述的就是类。同理,元类定义了类的行为(类方法),每当要向某个类发送一个消息时,都会通过 isa 指针找到该类所属的元类,然后遍历其方法缓存表或者方法列表,通过 SEL 找到后取出方法中的 IMP 函数入口指针,并执行该方法,否则进行消息转发阶段等等。

说到元类,那么定会有人问,元类的类是什么?元类也是一个对象,元类的类是根元类,根元类在继承体系中是根类的元类,那么根类的元类是属于哪个类呢?根元类的类就是自己(即 isa 指针指向自己),根元类的父类 superClass 指针指向 根类。

对象、类与元类的关系

Greg Parker (@gparker) 给出了一张图,使得整个结构清晰明了:

instance-class-meta_class

理解与探究

理解

如果上述都理解了,那么下面这段代码看懂就没问题了。 -(IMP)methodForSelector:(SEL)aSelector 是 NSObject 类的一个实例方法,以下两种调用方式都是正确的:

  • [NSObject methodForSelector:@selector(test)];
  • [[[NSObject alloc] init] methodForSelector:@selector(test)];

探究

为了验证本文的一些说法,下面我们写个测试代码进行验证,输出结果本文不做分析,有兴趣的读者可以对照上文自行分析:


Class currentClass = [UITextField class];
for (int index = 0; index < 5; ++ index) {
   
   NSLog(@"## index:%d ## isa:%p --- superClass:%p", index, currentClass, class_getSuperclass(currentClass));
   currentClass = object_getClass(currentClass);
}
NSLog(@"NSObject:%p --- NSObject Meta Class:%p\nMeta super Class:%p --- Meta root Class:%p", [NSObject class], object_getClass([NSObject class]), class_getSuperclass(object_getClass([NSObject class])), object_getClass(object_getClass([NSObject class])));

运行上述代码后,输出结果如下:

instance-class-meta_class-loginfos

By the way, happy Children’s Day!🤡🤡🤡

参考链接

  1. What is a meta-class in Objective-C?

  2. Classes and metaclasses

广告

欢迎关注微信公众号

wechat-qrcode

最近的文章

iOS 输入限制之 InputKit

前言最近接手了两个 O2O 的老项目,其中的 Bug 也不言而喻,单看项目中的布局就有 n 种不同的方式,有用纯代码的,有用 Masonry 的,有用 VFL 的,也有用 Xib 的,更有用代码约束等等等,🐮。不扯远了,回归正题。由于这两个项目是 O2O 项目,因此针对输入组件的限制相比其他类型的项目要多一些,比如商品价格输入(如:保留3位整数,2位小数等)、买家留言字数限制、不能输入中文、不能输入英文、只能输入数字等等限制。于是输入限制 InputKit 诞生了!本文主要简单介绍 Inp...…

继续阅读
更早的文章

iOS 中网络请求同步

场景在开发过程中,有时候会遇到这样一些问题,比如: 在某些业务要求下,需发送同步请求。 在某些界面需请求多个接口,且各个接口返回的数据之间或者整体存在依赖关系。 ···那么在上述的这些场景下应如何发送网络请求?发同步请求 or 异步请求?请求嵌套?······本文将简单探究开发过程中网络请求同步的问题以及相关注意点。NSURLConnection 中的同步请求我们都知道 NSURLConnection 中有一个同步请求的 API :+ (NSData *)sendSynchronou...…

继续阅读