方法的本质
1.准备测试代码:
#import <Foundation/Foundation.h>
@interface ABPerson : NSObject
-(void)saySomething;
@end
@implementation ABPerson
-(void)saySomething
{
NSLog(@"%s",__func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
ABPerson *p = [ABPerson alloc];
[p saySomething];
NSLog(@"Hello, World!");
}
return 0;
}
2.编译成.pp文件
clang -rewrite-objc main.m -o main.cpp
3.打开main.cpp文件分析:
在入口
main方法中,调用了alloc方法和saySomething方法,在产生的c++代码中,都调用了objc_msgSend这个方法,并且传了两个参数,第一个是消息的接受者(id),第二个是方法编号(SEL)。
消息发送的函数定义如下:
id objc_msgSend(id self, SEL _cmd, ...);
所以他还是可以传更多的参数的
4.修改函数,添加两个参数
#import <Foundation/Foundation.h>
@interface ABPerson : NSObject
-(void)saySomething:(NSString *)a b:(NSString *)b;
@end
@implementation ABPerson
-(void)saySomething:(NSString *)a b:(NSString *)b
{
NSLog(@"%s",__func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
ABPerson *p = [ABPerson alloc];
[p saySomething:@"A" b:@"B"];
NSLog(@"Hello, World!");
}
return 0;
}
5.重新编译,并查看函数调用
所以结论就是:方法的本质就是消息的发送
使用objc_msgSend调用方法
既然方法调用的本质就是通过objc_msgSend发送消息,那么就用它来调用方法。
1.导入头文件
#import <objc/message.h>
调用方法代码:
objc_msgSend(p,@selector(saySomething));
如果报错,配置一下红框位置,请其设置为
NO
objc_msgSendSuper定义
objc_msgSendSuper定义:
OBJC_EXPORT void
objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
objc_msgSendSuper包含的头两个参数分别是结构体objc_super和方法编号SEL
结构体objc_super定义:
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 */
};
包含了一个接受者receiver和一个super_class
objc_msgSendSuper使用
1.定义一个ABTeacher继承ABPerson,并实现doSomething方法
@interface ABTeacher : ABPerson
-(void)doSomething;
@end
@implementation ABTeacher
-(void)doSomething
{
NSLog(@"%s",__func__);
}
@end
ABTeacher *t = [ABTeacher alloc];
struct objc_super ab_objc_super;
ab_objc_super.receiver = t;
ab_objc_super.super_class = ABTeacher.class;
objc_msgSendSuper(&ab_objc_super,@selector(doSomething));
ab_objc_super.super_class是从哪个类开始查找,如果改成ABPerson.class,就是从ABPerson查找,如果ABPerson没实现就抛出异常,也不会再查找ABTeacher。
objc_msgSend汇编分析
打开objc源码找到
objc_msgSend是在源码中汇编实现:
查看arm64架构的
cmp p0,:判断当前消息接收者是否为0b.le LNilOrTagged:如果支持tagged pointer,走这步b.eq LReturnZero:不支持tagged pointer,走这步,返回0ldr p13, [x0]:将消息接收者isa给p13GetClassFromIsa_p16 p13, 1, x0:将消息接收者的class存到p16
GetClassFromIsa_p16是如何通过isa找到class的?
因为
GetClassFromIsa_p16 p13, 1, x0,所以src是p13也就是isa,needs_auth是1执行ExtractISA,ExtractISA就是将isa&ISA_MASK的结果给$0,也就是将拿到的class给p16。
上面的过程就是通过消息接收者获取到class。
(未完)
补充
protocol分析
代码准备:
#import <Foundation/Foundation.h>
@protocol ABPersonDelegate <NSObject>
-(void)masterNB;
@end
@interface ABPerson : NSObject<ABPersonDelegate>
-(void)saySomething;
@end
#import "ABPerson.h"
@implementation ABPerson
-(void)masterNB
{
NSLog(@"%s",__func__);
}
-(void)saySomething
{
NSLog(@"%s",__func__);
}
@end
p/x ABPerson.class:打印Class首地址p (class_data_bits_t *)0x0000000100008868:首地址偏移32个字节拿到bits并将地址强转成class_data_bits_t类型p $1->data():调用class_data_bits_t中的data()函数p *$2:打印datap $3.protocols():调用protocols(),获取protocol列表p $4.list:拿到protocol_list_t首地址p $5.ptr:打印protocol_list_t首地址p *$6:打印protocol_list_t,发现count=1p $7.list[0]:打印第一个p (protocol_t *)$8:强转成protocol_t类型p *$9:查看protocol_t结构并找到mangledName为ABPersonDelegatep $10.instanceMethods:获取实例方法p $11.get(0).big():读取method_list_t中的实例方法