iOS中的class,SEL,IMP

2018-10-08  本文已影响0人  MichealXXX

最近在看一些底层的东西,之前一直看到SEL,IMP等概念也不曾深究,今天看了很多资料,就顺手整理一下,讲的不够细致和准确也希望大家可以评论提出来,共同成长。

什么是class

在说class定义之前我们不妨先打开#import<objc/objc.h>文件看一下它的代码

#if !OBJC_TYPES_DEFINED
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;
#endif

我们可以看到这是一个struct objc_object结构体,这个结构体的指针也就是我们开发中经常用到的id,这个结构体内只有一个成员变量,Class类型的isa,我们再点进去看看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;
/* Use `Class` instead of `struct objc_class *` */

可以看到这个class指向了一个叫做objc_class的结构体,所以由此我们可以知道Class被定义为一个指向objc_class的结构体指针,我们再看看objc_class结构体的内容,这时候我们就又要推出一个概念叫做元数据(metadata)

元数据就是objc_class内存放的内容,通过成员变量的名字我们可以大致推测,这里面存放了指向父类的指针super_class、类的名字、版本、实例大小、实例变量列表、方法列表(下面会说到)、缓存、遵守的协议列表等,从这些成员变量中我们可以看出这些成员变量就是一个实例对象构成的基本要素,所以我们可以知道类本身也是一个对象,我们称之为类对象。

而类对象里面也有一个class类型的变量isa,这个isa又指向哪里呢,这时候就又有一个概念叫做元类(metaclass),元类中保存了创建类对象以及类方法所需的所有信息。

我们可以通过下边这张图来梳理一下这个关系

实例/类对象的isa指针结构图
由此我们可以知道,一个对象实例可以通过isa访问类对象,而类对象又是通过isa访问其元类,superclass则用于访问其父类,整个逻辑形成了一个完美的循环图。
什么是SEL

上面我们了解了class,元类等概念,接下来我们在看SEL的概念前先了解一下上文提到过的方法列表。

在上面class讲解中我们提到了一个概念叫做方法列表struct objc_method_list * _Nullable * _Nullable methodLists,方法列表是objc_class中的一个成员变量,我们可以结合上文讲过的知识抽象出一个概念图

方法链表图

通过这张图我们是不是也更加理解了上文讲过的class的内容,圆形所代表的实例对象的第一个实例变量isa,它指向该类的类结构 The object’s class。而该类结构有一个指向其父类类结构的指针superclass, 以及自身消息名称(selector)/实现地址(address)的方法链表selector就是指 MethodSEL, address就是指MethodIMP

理解了这个结构之后我们再来看看方法链表的代码

typedef struct objc_method *Method;

typedef struct objc_ method {
    SEL method_name;
    char *method_types;
    IMP method_imp;
};

方法链表里面存储的是Method 类型的,一个方法 Method,其包含一个方法选标 SEL – 表示该方法的名称,一个types – 表示该方法参数的类型,一个 IMP - 指向该方法的具体实现的函数指针。

好现在我们再来看看什么是SELSEL 的定义为:

typedef struct objc_selector   *SEL;

它是一个指向 objc_selector指针,表示方法的名字/签名,SEL可以等同与C语言中的函数指针,在C语言中可以直接把函数名赋值给函数指针,而OC中只能做一个@selector来取,它的结果就是一个SEL类型,本质就是类方法的编号,名字。

不同的类可以拥有相同的selector,这个没有问题,因为不同类的实例对象performSelector相同的selector 时,会在各自的消息选标(selector)/实现地址(address) 方法链表中根据 selector 去查找具体的方法实现IMP, 然后用这个方法实现去执行具体的实现代码。这是一个动态绑定的过程,在编译的时候,我们不知道最终会执行哪一些代码,只有在执行的时候,通过selector去查询,我们才能确定具体的执行代码。

什么是IMP
typedef id (*IMP)(id, SEL, ...);

在讲解SEL时我们说到方法列表中的实现地址adress就是IMP,我们就很清楚地知道 IMP 的含义:IMP 是一个函数指针,这个被指向的函数包含一个接收消息的对象id(self 指针), 调用方法的选标 SEL (方法名),以及不定个数的方法参数,并返回一个id。也就是说IMP是消息最终调用的执行代码,是方法真正的实现代码 。我们可以像在C语言里面一样使用这个函数指针

NSObject 类中的methodForSelector:方法就是这样一个获取指向方法实现IMP 的指针,methodForSelector:返回的指针和赋值的变量类型必须完全一致,包括方法的参数类型和返回值类型。


void (*setter)(id, SEL, BOOL);
int i;
 
setter = (void(*)(id, SEL, BOOL))[target methodForSelector:@selector(setFilled:)];
 
for (i = 0; i < 1000; i++){
  setter(targetList[i], @selector(setFilled:), YES);
}

使用methodForSelector:来避免动态绑定将减少大部分消息的开销,但是这只有在指定的消息被重复发送很多次时才有意义,例如上面的for循环。

最后说一点扩展,在iOS中方法的调用都是在运行时动态绑定selector和address,所以我们经常说的runtime正是利用了这一点,通过改变SEL所绑定的IMP来进行方法的替换,还有在现在的iOS的ARC机制中编译器会自动替我们执行autorelease这个方法,由于会大量使用到autorelease方法,所以避免消耗内存,在执行时使用的便是methodForSelector

参考文章

关于CLASS , SEL, IMP的说明
深入理解iOS开发中的isa指针

上一篇下一篇

猜你喜欢

热点阅读