目录
- 一、消息机制
- 二、方法调用源码查看
- 三、objc_msgSend的执行流程
- 四、番外篇
一、简要说明OC消息机制(objc_msgSend执行流程)
OC中的方法调用,其实都是转换为objc_msgSend函数调用
objc_msgSend的执行流程可以分为3大阶段
- 消息发送
- 动态方法解析
- 消息转发
如果找不到合适的方法调用,会报错 unrecognized selector sent to instance 0x100555ad0
二、方法调用源码查看
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 创建消息接收者(receiver)
MJPerson *person = [[MJPerson alloc] init];
// 对象方法调用
[person personTest];
// 类方法调用
[MJPerson initialize];
}
return 0;
}
和以前一样,我们使用clang看一下源码
$ xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
- 2.1、对象方法调用 [person personTest];
// C++源码
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("personTest"));
// 删除强制转换后
objc_msgSend(person, sel_registerName("personTest"));
// sel_registerName 相当于 @selector,所以,最终我们可以写成
objc_msgSend(person, @selector(personTest));
- 2.2、类方法调用[MJPerson initialize];
// C++源码
((void (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MJPerson"), sel_registerName("initialize"));
// 删除强制转换后
objc_msgSend(objc_getClass("MJPerson"), sel_registerName("initialize"));
// 同理,最终我们可以写成
objc_msgSend([MJPerson class], @selector(initialize))
三、objc_msgSend的执行流程
- 3.1、消息发送阶段
objc-runtime-new.mm类中的lookUpImpOrForward方法我们可以看到其实现
// Try this class's cache.
// 从当前类缓存中查找
imp = cache_getImp(cls, sel);
if (imp) goto done;
// Try this class's method lists.
// 从当前类方法列表中查找
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
// 如果找到,就将它加入类的缓存中
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
// Try superclass caches and method lists.
// 从父类的缓存中查找
{
unsigned attempts = unreasonableClassCount();
// 一层一层向上查找(superClass->superSuperClass->...)
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
// 缓存父类的方法(下面顺带将父类方法缓存到自己的缓存中)
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
// 将父类的方法缓存到消息接收者里面
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// Superclass method list.
// 在父类方法列表中查找,并缓存到父类的缓存中
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
- 3.2、动态方法解析阶段
// No implementation found. Try method resolver once.
// 判断是否进行过动态方法解析,如果没有解析过,则执行括号中的内容
if (resolver && !triedResolver) {
runtimeLock.unlockRead();
_class_resolveMethod(cls, sel, inst);
runtimeLock.read();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
// 第一次解析后标记,第二次就不会进行解析了
triedResolver = YES;
// 回到消息发送阶段
goto retry;
}
// No implementation found, and method resolver didn't help.
// Use forwarding.
// 消息转发阶段
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlockRead();
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
_class_resolveInstanceMethod(cls, sel, inst);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
_class_resolveClassMethod(cls, sel, inst);
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
_class_resolveInstanceMethod(cls, sel, inst);
}
}
}
- 3.2.1、动态方法解析具体实现一:对象方法调用解析
@interface MJPerson : NSObject
- (void)test;
@end
#import "MJPerson.h"
#import <objc/runtime.h>
@implementation MJPerson
//- (void)test {
// NSLog(@"%s", __func__);
//}
- (void)other {
NSLog(@"%s", __func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
// 获取其他方法
Method otherThod = class_getInstanceMethod(self, @selector(other));
// 动态添加test方法实现
class_addMethod(self,
sel,
method_getImplementation(otherThod),
method_getTypeEncoding(otherThod));
// 返回yes代表有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
[person test];
}
return 0;
}
2019-09-30 19:45:46.065352+0800 Test12[18214:1032252] -[MJPerson other]
- 3.2.2、类方法调用解析
@interface MJPerson : NSObject
+ (void)test;
@end
#import "MJPerson.h"
#import <objc/runtime.h>
@implementation MJPerson
//+ (void)test {
// NSLog(@"%s", __func__);
//}
void c_other(id self, SEL _cmd) {
NSLog(@"c_other -%@ -%@", self, NSStringFromSelector(_cmd));
}
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(test)) {
// 第一个参数是 object_getClass(self)
class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
[MJPerson test];
}
return 0;
}
2019-09-30 19:48:41.825602+0800 Test12[18258:1034184] c_other -MJPerson -test
- 3.2.3、C语言函数动态解析
#import "MJPerson.h"
#import <objc/runtime.h>
@implementation MJPerson
//- (void)test {
// NSLog(@"%s", __func__);
//}
void c_other(id self, SEL _cmd) {
NSLog(@"c_other -%@ -%@", self, NSStringFromSelector(_cmd));
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
// 获取其他方法
class_addMethod(self, sel, (IMP)c_other, "v16@0:8");
// 返回yes代表有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
- 3.3、消息转发
- 3.3.1、消息转发(一):forwardingTargetForSelector(MJPerson 消息由 MJStudent处理)
@interface MJPerson : NSObject
- (void)test;
@end
@implementation MJPerson
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
// objc_msgSend(student, aSelector);
return [[MJStudent alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
#import "MJStudent.h"
@implementation MJStudent
- (void)test {
NSLog(@"%s", __func__);
}
@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
[person test];
}
return 0;
}
2019-09-30 19:59:13.819509+0800 Test12[18418:1039881] -[MJStudent test]
- 3.3.2、消息转发(二):forwardInvocation
MJPerson 的消息 forwardingTargetForSelector方法未能处理,进入下一步转发流程
@interface MJPerson : NSObject
- (void)test;
@end
#import "MJPerson.h"
#import "MJStudent.h"
@implementation MJPerson
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
return nil;
}
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 = [[MJStudent alloc] init];
[anInvocation invoke];
*/
// 方法二
[anInvocation invokeWithTarget:[[MJStudent alloc] init]];
}
@end
#import "MJStudent.h"
@implementation MJStudent
- (void)test {
NSLog(@"%s", __func__);
}
@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
[person test];
}
return 0;
}
2019-09-30 20:08:02.735260+0800 Test12[18511:1043993] -[MJStudent test]
四、番外篇
- 4.1、类方法转发到对象方法
#import "MJStudent.h"
@implementation MJStudent
+ (void)test {
NSLog(@"%s", __func__);
}
- (void)test {
NSLog(@"%s", __func__);
}
@end
#import "MJPerson.h"
#import "MJStudent.h"
@implementation MJPerson
+ (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
/**
objc_msgSend([[MJStudent alloc] init], @selector(test));
[[[MJStudent alloc] init] test]
*/
return [[MJStudent alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
[MJPerson test];
}
return 0;
}
2019-09-05 21:05:50.180158+0800 test[40754:500893] -[MJStudent test]
- 4.2、动态实现 set、get方法
@interface MJPerson : NSObject
@property (nonatomic, assign) int age;
@end
#import "MJPerson.h"
#import <objc/runtime.h>
@implementation MJPerson
@dynamic age;
void setAge(id self, SEL _cmd, int age) {
NSLog(@"age is %d", age);
}
int age(id self, SEL _cmd) {
return 120;
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(setAge:)) {
class_addMethod(self, sel, (IMP)setAge, "v@:I");
return YES;
} else if (sel == @selector(age)) {
class_addMethod(self, sel, (IMP)age, "i@:I");
}
return [super resolveInstanceMethod:sel];
}
@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
person.age = 20;
NSLog(@"person.age is %d", person.age);
}
return 0;
}
2019-09-07 18:40:20.740030+0800 test[1514:27406] age is 20
2019-09-07 18:40:20.740276+0800 test[1514:27406] person.age is 120
网友评论