ios runtime - 开篇

objc_msgSend

最近在搞工程中的安全拦截措施,自然而然的要用到消息的转发机制,在这里就简单的叙述一下消息的转发机制为后续的工作做铺垫。
首先OC采用的是消息结构,而非函数调用。二者的区别在于消息结构的语言在运行时执行的代码直到运行时才确定;甚至连接收消息的对象编译器也不关心,也是在运行时确定,这个过程叫作动态绑定。而函数调用执行的代码有编译器决定,只有在函数是多态的时候才在运行时通过虚拟方发表查询真正的方法实现。

objc对象调用某个方法可以这样写:

id returnValue = [objc sendMessage:para]; 

上述中objc叫做“接收者”,sendMessage叫做“选择子”。选择子和参数(para)合起来叫做“消息”。编译器会把这条消息编译成一条标准C函数,这个函数就是消息转发机制的核心:objc_msgSend,如下,可以看到每个OC的方法都有两个隐式参数id self, SEL cmd, 也就是接收者和选择子,你也可以认为OC的方法由C的函数的外加两个参数组成。

void objc_msgSend(id self, SEL cmd, ...) 

上面OC的方法转换成C函数如下:

id returnValue = objc_msgSend(objc,  
                              @selector(sendMessage:),  
                              para); 

下面我们来看下OC中的类:
首先来看下NSObject类,可以直接在xcode里面找到它的定义

@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}
.
.
.等一些方法
还可以看到NSObject类遵守了NSObject协议,协议中也是一些方法和属性的定义

特别让然注意的就是isa变量 那么来看下Class是什么?

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

可以看到Class其实就是一个名字叫objc_class结构体的指针的别名,也就是objc_class结构体的指针类型。那么isa就是个objc_class类型的指针变量了!

如果你细心你还会发现一个类型就是id:可以看到id是objc_object结构类型的指针。而objc_object结构体中只有一个Class类型的指针。

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

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

那么现在所有的问题都指向了objc_class,那么我们来看下这个结构体是什么。如下是OBJC2.0之前的定义:

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;
/* Use `Class` instead of `struct objc_class *` */

由上面的NSObject类定义可以看出,每一个对象都有一个指向其所属类的isa指针,通过该指针找到所属的类,然后会在所属类中的方法列表中寻找方法的实现,如果在方法列表中查到了和选择子名称相符的方法就会跳转到他的方法实现,如果找不到会向其父类的方法列表中查找,以此类推,直到NSObject类,如果还是查找不到就会执行“消息转发”操作。
另外为了保证消息机制的效率,每一个类都设置一个缓存方法列表,缓存列表中包含了当前类的方法以及继承自父类的方法,在查询方法列的时候,都会先查询本类的缓存列表,再去查询方法类别。这样当一个方法已经被调用过一次,下次调用就会很快的查询到并调用。
细心的同学应该已经发现了,在Class中也有一个isa指针。上面也已经说了,我们创建的对象中的isa指针指向所属的类,那么Class中的isa指针指向哪里呢?它指向的是类的元类(metaclass),看到这里你是否明白了一个点:类也是对象!!!这样就理解了类中方法的储存位置,减号方法即对象方法储存在类中,加号方法即类方法储存在类对象的类中(即元类中)。
下面附一个图片来说明其关系来更好的展示他们的关系:假设someClass instance继承至NSObject,可以看到isa指向的是类,super_class可以查找到其父类。
这里写图片描述

下面介绍一下其他的字段的意思:

Class super_class: 指向其父类
const char *name: 类名
long version: 类的版本信息,初始化默认为0,可以通过runtime函数class_setVersion和class_getVersion进行修改、读取
long info: 一些标识信息,如CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含对象方法和成员变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;
long instance_size :该类的实例变量大小(包括从父类继承下来的实例变量)
struct objc_ivar_list *ivars: 用于存储每个成员变量的地址
struct objc_method_list **methodLists : 与 info 的一些标志位有关,如CLS_CLASS (0x1L),则存储对象方法,如CLS_META (0x2L),则存储类方法
struct objc_cache *cache: 指向最近使用的方法的指针,用于提升效率
struct objc_protocol_list *protocols: 存储该类遵守的协议

Method

这里你还需要了解方法的结构,才能理解上述内容。

typedef struct objc_method *Method;

//OBJC2 之前的定义
struct objc_method {
    SEL method_name                                         
    char *method_types                                       
    IMP method_imp                                          
}  
  • SEL就是选择子,实质是方法名
  • method_types是参数
  • method_imp 是指向方法实现指针
    也就是说方法是选择子,参数,方法实现的结构体,知道了这一点就很容易理解通过选择子找到方法的具体实现,那么我们就可以在选择子查找方法实现的过程中做一些“手脚”,增加一些安全过滤或者说直接替换系统方法实现。
    读了本文是否对你消息转发机制有了基本的认识,欢迎指出文章不足与错误,欢迎关注作者。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值