前言:
前言1.方法的底层结构
struct method_t{
SEL name; //函数名
const char *types;//编码(返回值类型、参数类型)
IMP imp;//指向函数的指针(函数地址)
}
- SEL代表函数名,一般叫做选择器,底层结构跟char *类似
- 可以通过@selector() 合sel_registerName()获得
- 可以通过sel_getName()和NSStringFromSelector()转成字符串
- 不同类中相同名字的方法,所对应的的方法选择器是相同的
- IMP代表函数的具体实现
- types包含了函数的返回值、参数编码的字符串
iOS中提供了一个叫做@encode的指令,可以将具体的类型表示成字符串编码
关于typedef struct objc_method *Method;它其实等价于struct method_t;
前言2.方法缓存cachet cache 的底层结构
Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,函数名为key,函数地址为value,可以提高方法的查找速度
实现:
1.objc_msgSend执行流程01-消息发送
在Class结构体内跟方法相关的有两个底层结构:
1>struct objc_class中的 cachet cache //方法缓存
2>struct class_rw_t中的method_list_t *methods //方法列表
消息查找的顺序是先在receiverClass的cache中查找方法是否存在,不存在的话就在receiverClass方法列表中查找,没有的话就superClass的cache中查找,如果找到了就调用方法并将方法缓存到receiverClass的cache中,如果superClass的cache中没有找到,就在superClass的方法列表中查找,如果找到了就调用方法并将方法缓存到receiverClass的cache中,如果没有就查看上层是否还有superClass。。。
2.objc_msgSend执行流程02-动态方法解析
动态方法解析分为类方法和实例方法两种模式
1>实例方法
- (void)other
{
NSLog(@"%s---", __func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 获取其他方法,这里是self中有other这个方法的实现,如果其他类有某个方法的实现可以改成
//Method method = class_getInstanceMethod([XXXClass class], @selector(other));
Method method = class_getInstanceMethod(self, @selector(other));
// 动态添加test方法的实现
class_addMethod(self, sel,
method_getImplementation(method),
method_getTypeEncoding(method));
// 返回YES代表有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
//由于关于typedef struct objc_method *Method;等价于struct method_t,所以可以直接类型转换
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 获取其他方法
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];
}
//也可以添加C语言函数来实现
void c_other(id self, SEL _cmd)
{
NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 动态添加test方法的实现,v16@0:8可以简化成v@:
class_addMethod(self, sel, (IMP)c_other, "v16@0:8");
// 返回YES代表有动态添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
2>类方法
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 resolveClassMethod:sel];
}
他们的用法类似,只是class_addMethod的第一个参数,一个是类,一个是元类,个人理解是将消息拦截,然后给receiver动态添加方法的实现。
3.objc_msgSend执行流程03-消息转发
如果没有实现消息解析,就进入消息转发阶段
+ (id)forwardingTargetForSelector:(SEL)aSelector
{
// objc_msgSend([[Cat alloc] init], @selector(test))
// [[[Cat alloc] init] test]
if (aSelector == @selector(test)) return [[Cat alloc] init];
//如果return的是[Cat class],那么底层就是调用转发目标的类方法
// objc_msgSend([Cat class], @selector(test))
// [Cat test]
return [super forwardingTargetForSelector:aSelector];
}
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) return [NSMethodSignature signatureWithObjCTypes:"v@:"];
return [super methodSignatureForSelector:aSelector];
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"1123");
}
@end
生成方法签名还可以利用某个类的方法声明来实现
[[[XXXClass alloc] init] methodSignatureForSelector:aSelector];
至于+ (void)forwardInvocation:(NSInvocation *)anInvocation;
既可以什么都不做,也可以到用invocation指定的目标中找方法的实现。
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// 参数顺序:receiver、selector、other arguments
// int age;
// [anInvocation getArgument:&age atIndex:2];
// NSLog(@"%d", age + 10);
// anInvocation.target == [[Cat alloc] init]
// anInvocation.selector == test:
// anInvocation的参数:15
// [[[Cat alloc] init] test:15]
[anInvocation invokeWithTarget:[[Cat alloc] init]];
int ret;
[anInvocation getReturnValue:&ret];
NSLog(@"%d", ret);
}
消息转发阶段会再次执行objc_msgSend的第一步!!!
补充:super的本质
super调用,底层会转换成objc_msgSendSuper2函数的调用,接收2个参数
1.>struct objc_super2
2.>SEL
而objc_super2的结构是
struct objc_super2{
id receiver;
CLass current_class;
}
reveicer是消息接受者,current_class是receiver的Class对象。这里可以解释[self class]与[super class]的区别。super调用仅仅表明查找方法是从父类开始的而已。