Runtime 相关术语(二)

一.Runtime相关术语

1.SEL

它是 selector 在 Objc 中的表示(swift 中是Selector 类)。selector是方法选择器,它的作用就和名字是一样的,在日常生活中,我们通过人名辨别谁是谁,注意 Objc 在相同类中不会有命名相同的两个方法。selector对方法名进行包装,以便找到对应的方法实现。它的数据结构是: typedef struct objc_selector *SEL; 我们可以看出它是个映射到方法的C字符串,可以通过 Objc 编译器命令@selector() 或着 Runtime 系统的sel_registerName函数来获取一个SEL类型的方法选择器。注意:不同类中相同名称的方法对应的 selector是相同的,由于变量类型不同,所以导致他们调用方法实现混乱

2.id

id 是一个参数类型,它是指向某个类的实例的指针。定义如下:

typedef struct objc_object *id;

struct objc_object *{Class isa;};

以上定义可以看到objc_object结构体包含一个isa指针,根据isa指针就可以找到对象所属类型。

注意:isa指针在代码运行时并不总是指向实例对象所属的类型,所以不能依靠他来确定类型,要想确定类型还是需要用对象的 -class方法。PS:KVO的实现机制,就是将观察者对象的isa指针指向一个中间类而不是真实的类型。

3.Class

typedef struct objc_class *Class;

Class 其实就是指向objc_class 结构体的指针。objc_class的数据结构如下

struct objc_class{
    Class isa OBJC_ISA_AVAILABILITY;
    #if !_OBJC2_    
    Class super_class                          OBJC2_UNAVAILABLE;
    const char *name                           OBJC2_UNAVAILABLE;
    long version                               OBJC2_UNAVAILABLE;
    long info                                  OBJC2_UNAVAILABLE;
    long instance_size                         OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars               OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists      OBJC2_UNAVAILABLE;
    struct objc_cache *cache                   OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols       OBJC2_UNAVAILABLE;
#endif
}OBJC2_UNAVAILABLE;

从objc_class 可以看到,一个运行时类中关联了它的父类指针、类名、成员变量、方法、缓存以及附属协议。

其中objc_ivar_list 和 objc_method_list 分别是成员变量和方法列表:

// 成员变量列表
strcut objc_ivar_list{
    int ivars_count                         OBJC2_UNAVAILABLE;
#ifdef _LP64_
    int space                               OBJC2_UNAVAILABLE;
#endif
    /* variable length structure*/    
    struct objc_ivar ivar_list[1]           OBJC2_UNAVAILABLE;    
}                                           OBJC2_UNAVAILABLE;

// 方法列表
strcut objc_method_list{
    struct objc_method_list *obsolete       OBJC2_UNAVAILABLE;
    
    int method_count                        OBJC2_UNAVAILABLE;
#ifdef _LP64_
    int space                               OBJC2_UNAVAILABLE;
#endif
/*variable length structure*/
struct objc_method method_list[1]           OBJC2_UNAVAILABLE;          
}

由此可见,我们可以动态修改*methodlist的值来添加成员方法,这也是category 实现的原理,同样解释了category不能添加属性的原因。

objc_ivar_list 结构体用来存储成员变量的列表,而objc_ivar则存储了单个成员变量的信息;同理,objc_method_list的结构体存储了方法数组的列表,而单个方法的信息则由objc_method结构体来存储。

值得注意的是,objc_class 中也有一个isa指针,这说明了Objc类本书也是一个对象。为了处理类和对象的关系,Runtime库创建了一种叫Meta Class(元类)的东西,类对象所属的类叫做元类。Meta Class 表述了类对象本书所具备的元数据。

我们所熟悉的类方法,就源自于 Meta Class。我们可以理解为类方法就是类对象的实例方法。每个类仅有一个类对象,而每个类对象仅有一个与之相关的元类。

当你发一个类似 [NSObject alloc]的消息时,实际上,这个消息被发送给一个类对象(Class Object),这个类对象必须是元类的实例,而这个元类同时也是一个根元类(Root Meta Class)的实例。所有元类的isa指针最终都指向根元类。

所以当 [NSObject alloc] 这条消息发送给类对象的时候,运行时代码 objc_msgSend() 会去它的元类中查找对应的能够响应消息的方法实现,如果找到了,就会对这个类对象执行方法调用。

最后 objc_class 中还有一个objc_cache 缓存,后面介绍。

4.Method

method 代表类中某个方法的类型

typedef struct objc_method *Method;
struct objc_method{
    SEL method_name                    OBJC2_UNAVAILABLE;
    char *method_types                 OBJC2_UNAVAILABLE;
    IMP method_imp                     OBJC2_UNAVAILABLE;
}

objc_method 存储了方法名,方法类型和方法实现;

方法名类型为 SEL,方法类型 method_types 是个 char 指针,存储方法的参数类型和返回值类型,meethod_imp 指向了方法的具体实现,本质上是一个函数指针。

5.IMP

IMP 在 objc.h 中的定义是: typedef id (*IMP)(id,SEL,...);

它是一个函数指针,这是由编译器生成的,当你发起一个 Objc 的消息后,最终他会执行的那段代码,就是由这个函数指针指定的。而这个 IMP 这个函数指针就是指向了这个方法的具体实现。

如果的得了执行某个实例某个方法的入口,我们就可以绕开消息传递阶段,直接执行方法。

你会发现 IMP 指向的方法与 objc_msgSend 函数类型相同,参数都包含 id 和 SEL 类型。每个方法名都对应一个 SEL 类型的方法选择器,而每个实例对象中的 SEL 对应的方法实现肯定是唯一的,通过一组 id 和 SEL 参数就能确定唯一的方法实现地址。

而一个确定的方法也只有唯一一组的 id 和 SEL 参数。

6. Cache 

//cache 定义如下
 typedef strcut objc_cache *Cache
struct objc_cache {
    unsigned int mask /* total = mask + 1*/        OBJC2_UNAVAILABLE;
    unsigned int occupied                          OBJC2_UNAVAILABLE;
    Method buckets[1]                              OBJC2_UNAVAILABLE;
};

Cache 为方法调用的性能进行优化,每当实例对象接收到一个消息时,他不会直接在 isa 指针指向的类的方法列表中遍历查找能够响应的方法,美味每次都要查询效率太低,而是优先在Cache中查找。

Runtime 系统会把调用的方法存到 Cache中,如果一个方法被调用,那么它可能今后还会被调用,下次查找的时候效率就会更高。就像计算机组成原理中的 CPU 绕过主存先访问 Cache 一样。

typedef strcut objc_property *Property;

typedef struct objc_property *objc_property_t;// 这个梗常用

可以通过 class_copyPropertyList 和protocol_copyPropertyList 方法获取类和协议中的属性;

objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)

objc_property_t *protocol_copyPropeertyList(Protocol *proto, unsigned int *outCount)

注意:返回的实行列表,列表中的每个元素都是一个 objc_property_t 指针

#import <Foundation/Foundation.h>
@interface Peroson : NSObject

/** 姓名 **/
@property (strong, nonatomic) NSString *name;
/** age **/
@Property (assign, nonatomic) int age;
/** weight **/
@property (assign, nonatomic) double weight;

@end

这是一个Person 类,有3个属性,用上述方法获取类的运行时属性

unsigned int outCOunt = 0;
objc_property_t *properties = class_copyPropertyList([Person class],&outCount);
NSLog(@"outCount = %d",outCount);
for (NSInteger i = 0; I < outCount; i++){
    NSString *name = @(property_getName(properties[i]));
    NSString *attributes = @(property_getAttributes(properties[i]));
    NSLog(@"%@------%@",name,attributes);
}

打印结果如下:

outCount = 3
name------T@"NSString",&,N,V_name
age------Ti,N,V_age
weight------Td,N,V_weight

property_getName 用来查找属性的名称,返回 c 字符串。property_getAttributes 函数用来挖掘属性的真实名称和@encode 类型,返回 c 字符串。

objc_property_t class_getProperty(Class cls, const char *name)
objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)
class_getProperty 和 protocol_getProperty 通过给出属性名在类和协议中获得属性的引用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值