iOS 开发 Objective-C

iOS 底层 day13 runtime 消息发送 动态解析 消

2020-09-05  本文已影响0人  望穿秋水小作坊

objc_msgSend 的执行流程可以分为三大阶段:①消息发送 ②方法动态解析 ③消息转发

一、objc_msgSend 源码最重要的方法图解

1. lookUpImpOrForward(cls, sel, obj, YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
lookUpImpOrForward 核心函数图解

二、objc_msgSend 消息发送

objc_msgSend 消息发送(第一阶段)
1.思考 [nil test] 调用会报错吗?

三、objc_msgSend 动态方法解析

objc_msgSend 动态方法解析
// Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
-(void)noImplementMethod;
@end


// Person.m
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
void dynamicResolveMethod(id self, SEL _cmd) {
    printf("dynamicResolveMethod \n");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(noImplementMethod)) {
        class_addMethod(self, sel, (IMP)dynamicResolveMethod, "v16@0:8");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

//+ (BOOL)resolveClassMethod:(SEL)sel
@end

四、objc_msgSend 消息转发

objc_msgSend 消息转发图解
1.用消息转发流程来实现 [person run:10] 调用 [cat run:10]
// person.m
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(run:)) {
        return [[Cat alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}
// person.m
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(run:)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:i"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    anInvocation.target = [[Cat alloc] init];
    [anInvocation invoke];
}
2.用消息转发流程来实现 [person run:10] 调用 [cat walk:10]
// person.m
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(run:)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:i"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    anInvocation.target = [[Cat alloc] init];
    anInvocation.selector = sel_registerName("walk:");
    [anInvocation invoke];
}
3.用消息转发流程来person无论调用什么方法都不报错的机制
// person.m
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    printf("%s 找不到方法\n", sel_getName(anInvocation.selector));
}

五、一些综合性问题

1. 解释一下什么情况下会出现 unrecognize selector sent to instance?
2. @dynamic有什么作用? 如何动态添加getsetter方法?

六、消息机制 objc_msgSend 源码解读流程

Person *person = [[Person alloc] init];
[person personTest];
///objc_msgSend(person, @selector(personTest));
1. objc-msg-arm64.s (入口文件)
1.01. ENTRY _objc_msgSend 函数入口
1.02. b.le LNilOrTagged
1.03. CacheLookup NORMAL
1.04. .macro CacheLookup
1.05. .macro CacheLookup
1.06. STATIC_ENTRY __objc_msgSend_uncached
1.07. .macro MethodTableLookup
1.08. .macro MethodTableLookup
1.09. bl __class_lookupMethodAndLoadCache3
2. objc-runtime-new.mm (入口文件)
2.01. IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
2.02. lookUpImpOrForward(cls, sel, obj, YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
lookUpImpOrForward 核心函数图解
上一篇 下一篇

猜你喜欢

热点阅读