美文网首页runtime
iOS底层原理笔记 - objc_msgSend方法的调用

iOS底层原理笔记 - objc_msgSend方法的调用

作者: xlii | 来源:发表于2022-08-11 16:37 被阅读0次

1.方法的本质

通过之前分析对象的本质,同理,我们也可以通过clang来分析方法的本质。

首先创建Person类,在Person类中我们先实现一个方法:

.h

@interface Person : NSObject

- (void)sayHi;

@end

.m

#import "Person.h"

@implementation Person

- (void)sayHi {
    NSLog(@"hi");
}

@end

然后我们在main函数中调用一下person的方法:

Person *person = [[Person alloc] init];
[person sayHi];

随后转成C++代码看下内部实现:

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp

打开main.cpp文件,可以全局搜索我们的“sayHi”方法,查看

转换前:
Person *person = [[Person alloc] init];
[person sayHi];

转换后:
Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
        ((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayHi"));

可以看出,实例对象调用方法,本质是通过objc_msgSend(消息发送)实现的。

接下来我们来验证一下它们是不是真的一样:

Person *person = [[Person alloc] init];
//方法调用
[person sayHi];
//消息发送
objc_msgSend(person, @selector(sayHi));

打印结果如下,两种打印结果一致,验证cheng

Untitled.png

注意: 1.调用objc_msgSend,需要导入头文件 #import <objc/message.h>

2.使用objc_msgSend时,编译器会报错。需要将 target —> Build Setting —> 搜索msg —> 将Enable Strict Checking of objc_msgSend(Calls objc_msgSend的严格检查机制)关掉

Untitled 1.png

2.方法的调用

下面的代码,Student 继承自 Person,Person 实现了 sayHi 方法,Student 确没有实现。

@interface Person : NSObject

- (void)sayHi;

@end

@implementation Person

- (void)sayHi {
    NSLog(@"hi");
}

@end

@interface Student : Person

- (void)sayHi;
- (void)sayHello;

@end

@implementation Student

- (void)sayHello {
    NSLog(@"hello");
}

@end

消息的接收者是 Student的实例,分别使用 OC 对象调用和 C语言的 API 调用:

  1. OC对象的调用
Person *person = [[Person alloc] init];
Student *student = [[Student alloc] init];
[student sayHi];
  1. 直接调用底层C的API
struct objc_super stusuper;
stusuper.receiver = student;
stusuper.super_class = [Person class];
objc_msgSendSuper(&stusuper, sel_registerName("sayHi"));

objc_msgSendSuper 的定义:

OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * super, SEL op, ...)

#endif
/// 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  兼容旧的objc-runtime.h头文件 */  
    __unsafe_unretained _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
    /* super_class is the first class to search */
};
#endif

OBJC2中,需要传入 receiver 和 super_class。

运行,打印结果一致:

2022-08-10 17:14:14.914094+0800 objc[18959:210368] hi
2022-08-10 17:14:14.914596+0800 objc[18959:210368] hi

可见,Student 并没有实现 sayHi,却没有崩溃,而且两种方法都打印出来了,猜测是因为其父类实现了sayHi。

可猜想,类的实例方法的调用是会像父类查找。而方法的本质是 objc_msgSend 发送消息,那么它是怎么通过 sel 找到 imp 函数地址的指针 ,从而找到函数的具体实现呢?

带着这个疑惑我们来查看 objc_msgSend 的流程。

objc_msgSend快速查找流程

相关文章

网友评论

    本文标题:iOS底层原理笔记 - objc_msgSend方法的调用

    本文链接:https://www.haomeiwen.com/subject/ylxfgrtx.html