IOS底层探索方法的本质

方法的本质

1.准备测试代码:c++

#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文件git

clang -rewrite-objc main.m  -o main.cpp
复制代码

3.打开main.cpp文件分析: 图片.png 在入口main方法中,调用了alloc方法和saySomething方法,在产生的c++代码中,都调用了objc_msgSend这个方法,而且传了两个参数,第一个是消息的接受者(id),第二个是方法编号(SEL)。github

消息发送的函数定义以下:markdown

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.从新编译,并查看函数调用 图片.pngoop

图片.png

因此结论就是:方法的本质就是消息的发送测试

使用objc_msgSend调用方法

既然方法调用的本质就是经过objc_msgSend发送消息,那么就用它来调用方法。ui

1.导入头文件spa

#import <objc/message.h>
复制代码

调用方法代码:

objc_msgSend(p,@selector(saySomething));
复制代码

图片.png 若是报错,配置一下红框位置,请其设置为NO 图片.png

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));
复制代码

图片.png ab_objc_super.super_class是从哪一个类开始查找,若是改为ABPerson.class,就是从ABPerson查找,若是ABPerson没实现就抛出异常,也不会再查找ABTeacher

objc_msgSend汇编分析

图片.png 打开objc源码找到objc_msgSend是在源码中汇编实现: 查看arm64架构的

图片.png

  • cmp p0,:判断当前消息接收者是否为0
  • b.le LNilOrTagged:若是支持tagged pointer,走这步
  • b.eq LReturnZero:不支持tagged pointer,走这步,返回0
  • ldr p13, [x0]:将消息接收者isap13
  • GetClassFromIsa_p16 p13, 1, x0:将消息接收者的class存到p16

GetClassFromIsa_p16是如何经过isa找到class的?

图片.png 图片.png 由于GetClassFromIsa_p16 p13, 1, x0,因此srcp13也就是isaneeds_auth1执行ExtractISA,ExtractISA就是将isa&ISA_MASK的结果给$0,也就是将拿到的classp16

上面的过程就是经过消息接收者获取到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
复制代码

图片.png 图片.png

  • 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:打印data
  • p $3.protocols() :调用protocols(),获取protocol列表
  • p $4.list:拿到protocol_list_t首地址
  • p $5.ptr :打印protocol_list_t首地址
  • p *$6 :打印protocol_list_t,发现count=1
  • p $7.list[0]:打印第一个
  • p (protocol_t *)$8:强转成protocol_t类型

图片.png

  • p *$9:查看protocol_t结构并找到 mangledNameABPersonDelegate
  • p $10.instanceMethods:获取实例方法
  • p $11.get(0).big():读取 method_list_t中的实例方法