文章目录
message.h
概述
objc_super
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
};
#endif
这里定义了一个结构体objc_super。
这个结构体里有两个实例对象,一个是id类型的receiver,一个是Class(Class的定义是在objc.h中,Class是objc_class类型的结构体,objc_class在runtime.h中)类型的super_class。
receiver其实就是self,根据注释是指定了类的实例,即自己。
super_class即父类,指定了类的父类。
这个结构体的作用是什么呢?大概猜测一下,既然放在message.h里面,应该和消息转发有关。消息转发的时候指定谁去转发消息,首先是receiver,也即时self,然后是super_class,消息也传递到了指定的父类那里。
objc_msgSend、objc_msgSendSuper
这个类里面定义了一些objc_msgSend()函数,还有objc_msgSendSuper()。
objc_msgSend这个比较常见
((void (*)(id, SEL))objc_msgSend)(p, @selector(run));
这以下的函数我们并不常见,暂时不清除他们的作用,但是查查资料可以学习学习,既然和objc_msgSend都定义在message.h中,那可能也是做消息转发用的,objc_msgSend可以发送消息,下面这些方法当然也是用来发送消息的,只是在特定的其情况下runtime使用下面一些函数进行消息转发。
正常情况下,runtime会使用函数objc_msgSend()去转发消息。当本实例没找到对应的方法时,会去父类找,如果找到了就使用objc_msgSendSuper()去转发消息,如果还是未找到方法,会使用下面的一下函数进行消息转发。
objc_msgSend_stret、objc_msgSendSuper_stret
看这个函数的文档注释,感觉不像是抛出异常才会被使用。
它应该和objc_msgSend这个是“同一级”的函数。
stret是个什么?
看了看网上有人说stret就是struct return的缩写,🤣。
那这个函数就是提供返回一个结构体的消息转发功能的函数吧。
怎么用呢?还是以Person举例:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
struct Measurements {
CGFloat waistline; //腰围
CGFloat bust; //胸围
CGFloat hipline; //臀围
};
typedef struct Measurements Measurements;
static inline Measurements MeasurementsMake(CGFloat waistline,CGFloat bust,CGFloat hipline){
Measurements info;
info.waistline = waistline;
info.bust = bust;
info.hipline = hipline;
return info;
};
@interface Person : NSObject
@property (nonatomic) CGFloat height; //身高
- (Measurements)getMeasurementsInfo;
@end
#import "Person.h"
@implementation Person
- (Measurements)getMeasurementsInfo {
return MeasurementsMake(60, 100, 100);
}
@end
在Person里我们自定义了一个存储用户三围信息的结构体Measurements,对外提供了一个获取三围信息的方法- (Measurements)getMeasurementsInfo;
那么我们使用Runtime的objc_msgSend怎么调用呢?
这个方法的返回值是一个结构体Measurements,所以我们使用objc_msgSend_stret()函数。
使用message里的函数需要先引入头文件#import <objc/message.h>
//初始化实例,直接调用方法
Person *p = [[Person alloc] init];
Measurements info = [p getMeasurementsInfo];
NSLog(@"%f,%f,%f",info.waistline,info.bust,info.hipline);
//使用objc_msgSend_stret()来发送消息
SEL sel = @selector(getMeasurementsInfo); //获取方法编号SEL
Measurements info = ((Measurements (*)(Person *, SEL, NSString*))objc_msgSend_stret)(p, sel, @"getMeasurementsInfo");
NSLog(@"%f,%f,%f",info.waistline,info.bust,info.hipline);
上面两种的调用方式是等价的,OC方法调用,最终会转成下面这种Runtime函数来做消息转发。
objc_msgSend_fpret
如果消息返回的是浮点数,使用这个函数发送消息。比如以获取身高为例:
直接获取:
CGFloat height = p.height;
//CGFloat height = [p height];
以上两种获取都是等价的,都是通过get方法获取属性。
在Runtime底层是这样进行消息转发的:
CGFloat height = ((CGFloat (*)(Person *,SEL))objc_msgSend_fpret)(p,@selector(height));
这个函数的做如下解析:
height不用说了是返回值
(Person *,SEL) 是发送方法传递的参数
Person * 表明是Person类型的实例发送消息
SEL是传递一个方法地址的SEL
(CGFloat (*)(Person *,SEL))这个整体作为函数objc_msgSend_fpret()前面部分,定义形式参数,即形参。
(p,@selector(height))作为函数后半部分,进行传实际参数,即实参。
((返回值类型 (*) (这里是形式参数其中SEL))objc_msgSend_fpret) (实参)
(*)
是做什么用?是函数的标识符?
下面这三个是方法做什么用的呢?method_invoke、msgForward感觉像是做消息转发,动态消息处理用的吧。这里先放着吧,后面看能不能碰到。