- OC中的方法的调用,其实都是转换为objc_msgSend函数的调用
- 继续调用进阶回顾二的
SFPerson对象,我们可以理解为person的run方法调用最终在c语言的如下方法调用:
objc_msgSend(person, @selector(run));
消息接收者(receiver):person
消息名称:run
- objc_msgSend的执行流程:
消息发送 -> 动态解析 -> 消息转发
查看源码
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
// 寄存器:消息接受者 receiver
cmp p0, #0 // nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
b.eq LReturnZero
#endif
ldr p13, [x0] // p13 = isa
GetClassFromIsa_p16 p13 // p16 = class
LGetIsaDone:
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
// 查找缓存
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
b.eq LReturnZero // nil check
// tagged
adrp x10, _objc_debug_taggedpointer_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
ubfx x11, x0, #60, #4
ldr x16, [x10, x11, LSL #3]
adrp x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
add x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
cmp x10, x16
b.ne LGetIsaDone
// ext tagged
adrp x10, _objc_debug_taggedpointer_ext_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_ext_classes@PAGEOFF
ubfx x11, x0, #52, #8
ldr x16, [x10, x11, LSL #3]
b LGetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif
LReturnZero:
// x0 is already zero
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
ret 相当于C语言的return
END_ENTRY _objc_msgSend
- 通过
CacheLookup NORMAL方法进行缓存中查找,如果没有找到则从STATIC_ENTRY __objc_msgSend_uncached方法查找MethodTableLookup查找_class_lookupMethodAndCache3进行查找,然后查找IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver)有点绕了
3633CBC2E1B7A08BC498A0172CADD441.png - 查看方法是否在当前类对象的缓存中,如果没有就进行查找调用
Method meth = getMethodNoSuper_nolock(cls, sel);方法查找
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
assert(cls->isRealized());
// fixme nil cls?
// fixme nil sel?
for (auto mlists = cls->data()->methods.beginLists(),
end = cls->data()->methods.endLists();
mlists != end;
++mlists)
{
method_t *m = search_method_list(*mlists, sel);
if (m) return m;
}
return nil;
}
该方法又使用method_t *m = search_method_list(*mlists, sel);进行查找 发现search_method
static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
return findMethodInSortedMethodList(sel, mlist);
} else {
// Linear search of unsorted method list
for (auto& meth : *mlist) {
if (meth.name == sel) return &meth;
}
}
#if DEBUG
// sanity-check negative results
if (mlist->isFixedUp()) {
for (auto& meth : *mlist) {
if (meth.name == sel) {
_objc_fatal("linear search worked when binary search did not");
}
}
}
#endif
return nil;
}
再查找到 sorted方法
static method_t *findMethodInSortedMethodList(SEL key, const method_list_t *list)
{
assert(list);
const method_t * const first = &list->first;
const method_t *base = first;
const method_t *probe;
uintptr_t keyValue = (uintptr_t)key;
uint32_t count;
for (count = list->count; count != 0; count >>= 1) {
probe = base + (count >> 1);
uintptr_t probeValue = (uintptr_t)probe->name;
if (keyValue == probeValue) {
// `probe` is a match.
// Rewind looking for the *first* occurrence of this value.
// This is required for correct category overrides.
while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
probe--;
}
return (method_t *)probe;
}
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
return nil;
}
分别使用二分法和线性查找进行查找
-
消息转发流程图如下:
228F997CCF8C839E11374E1D38CE0FA9.png - 总结:首先判断消息接受者
receiver是否为nil,如果为nil直接退出,如果不为nil则从当前接受者类对象receiverClass的缓存cache中查找方法,如果查找到就调用,如果没有查找到,去当前接受者类对象的class_rw_t中查找方法,找到调用,没找到去父类的缓存cache中查找,如果找到调用,没找到去父类的class_rw_t中查找方法,找到调用,没找到就行动态方法解析
动态方法解析
- 方法解析,新建一个
SFPerson类增加personRun方法
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface SFPerson : NSObject
+(void)personRun;
@end
NS_ASSUME_NONNULL_END
#import "SFPerson.h"
#import <objc/runtime.h>
@implementation SFPerson
struct method_t{
SEL sel;
char *types;
IMP imp;
};
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(personRun)) {
// 获取其他方法
struct method_t *method = (struct method_t *)class_getInstanceMethod(self, @selector(other));
// 动态添加test方法的实现
class_addMethod(self, sel, method->imp, method->types);
// 返回YES代表有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
-(void)other{
NSLog(@"%s",__func__);
}
@end
- 调用
[SFPerson personRun]方法我们会得到other方法的调用,就是我们进行动态方法解析,把消息的实现解析到other方法进行操作. - 其中
struct method_t和Method是一样的所以第二种实现方式:
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(personRun)) {
// 获取其他方法
//struct method_t 和 Method是一样的
Method method = class_getInstanceMethod(self, @selector(other));
// struct method_t *method = (struct method_t *)class_getInstanceMethod(self, @selector(other));
//动态添加方法
// class_addMethod(self, sel, method->imp, method->types);
class_addMethod(self, sel, method_getImplementation(method),method_getTypeEncoding(method));
//返回YES代表动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
- 还可以使用
c语言的函数实现如下:
void c_other(id self,SEL _cmd){
NSLog(@"c_other-%@ -%@",self,NSStringFromSelector(_cmd));
}
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(personRun)) {
//动态添加方法
class_addMethod(self, sel, (IMP)c_other, "v16@0:8");//v void空返回类型 16 两个指针16个字节 @第一个参数 对象类型 从0开始 :第二个参数 从第8个字节开始
//返回YES代表有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
- 如果我们调用的是类方法,那么其实现如下:
void c_other(id self, SEL _cmd)
{
NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}
+ (BOOL)resolveClassMethod:(SEL)sel
{
if (sel == @selector(personRun)) {
// 第一个参数是object_getClass(self)
class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
return YES;
}
return [super resolveClassMethod:sel];
}
- 动态解析总结:
- 如果没有动态解析过,则调用
+ (BOOL)resolveClassMethod:(SEL)sel或者+(BOOL)resolveInstanceMethod:(SEL)sel方法进行解析,调用对应的方法
1F612CEE19586417D1E7EE9D0319BDDF.png - 如果动态解析标记为已解析但是还是没有对应方法的实现则进行消息转发
消息转发
- 上代码:
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
// objc_msgSend([[SFCat alloc] init], aSelector)
return [[SFCat alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
// 方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
}
return [super methodSignatureForSelector:aSelector];
}
// NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
// anInvocation.target 方法调用者
// anInvocation.selector 方法名
// [anInvocation getArgument:NULL atIndex:0]
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// anInvocation.target = [[SFCat alloc] init];
// [anInvocation invoke];
[anInvocation invokeWithTarget:[[SFCat alloc] init]];
}
-
首先调用
forwardingTargetForSelector:方法,返回不是nil则进行objc_msgSend流程,如果返回值为nil则进行调用methodSignatureForSelector:方法,如果不反悔nil则调用forwardInvocation:方法的调用,如果还是返回nil则调用doesNotRecognizeSelector:方法到此,消息转发完成报出异常。
65F4944E8A0A4BEF2E290FC98898612D.png 如果调方法有参数输入和返回值:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// 参数顺序:receiver、selector、other arguments
// int age;
// [anInvocation getArgument:&age atIndex:2];
// NSLog(@"%d", age + 10);
// anInvocation.target == [[SFCat alloc] init]
// anInvocation.selector == test:
// anInvocation的参数:15
// [[[SFCat alloc] init] test:15]
[anInvocation invokeWithTarget:[[SFCat alloc] init]];
int ret;
[anInvocation getReturnValue:&ret];
NSLog(@"%d", ret);
}



