美文网首页
消息转发 :objc_msgSend

消息转发 :objc_msgSend

作者: SinceMeYou | 来源:发表于2020-03-01 20:20 被阅读0次

objc_msgSend 是OC中用于方法的调用,了解其底层的调用能更好的让我们了解其调用的过程,以及针对消息转发的过程进行拦截,可以做一些防止App崩溃的工作。

Objective-C 方法Method的本质

本质是发送消息,任何的方法的调用都会编译成消息。
消息的组成:
objc_msgSend(id self, SEL _cmd, ...)
消息接受者,消息编号(方法名字),参数

objc_msgSend(id self, SEL _cmd, ...)
消息的组成由 id self,SEL _cmd 以及后续的参数    
其中,
id self 可以理解为调用方法的对象,消息接受者,   
即下文中的 "person"。
SEL _cmd 为方法名,传入类似   
sel_registerName("MethodName"),即下文中的"   
[person sendMessage];"方法。
若方法中需要传入参数,就在后面拼接,即下文中   
的 "[person sendMessage:]"方法。
获取SEL的三种方法:
1. Runtime提供的          sel_registerName
2. Objective-C编译器提供的 @selector()
3. NSObject提供的         NSSelectorFromString()
SEL与IMP(implementation)的关系:
sel_registerName("MethodName")为方法编号,    
通过方法编号找到方法对应的IMP是函数实现的    
指针, 最后直接调用函数。
  • 实例方法调用

    Person *person = [[Person alloc]init];
    在底层会转化为这样:
    Person *person = ((Person * (*)(id,SEL))objc_msgSend)((id)[Person class],@selector(alloc));
    s = ((Person * (*)(id,SEL))objc_msgSend)(person,@selector(init));
    [person sendMessage];
    底层会转换为:
    (( void(*)(id,SEL))objc_msgSend)(person,@selector(sendMessage));
    一般我们知道的是这样的:
    objc_msgSend(person, @selector(sendMessage));
    //带参数
    [ person sendMessage:@"Hello World"];
    objc_msgSend(person, sel_registerName("sendMessage:"), "Hello World");
    
  • 类方法调用

      [Person send];
      objc_msgSend(objc_getClass("Person"), sel_registerName("send"));
    
      //带参数
      [Person send:@"Hello World"];
      objc_msgSend(objc_getClass("Person"), sel_registerName("send:"), "Hello World");
    
  • 如何给父类发消息

     // 向父类发消息(对象方法)
     struct objc_super superClass;
     superClass.receiver = person; //确定这个消息发送的对象,即发送给s的父类 
     superClass.super_class = class_getSuperclass([person class]);
     objc_msgSendSuper(& superClass, sel_registerName("send"));
    
     //向父类发消息(类方法)
     struct objc_super superClass;
     superClass.receiver = [person class];
     superClass.super_class = class_getSuperclass(object_getClass([person class]));
     objc_msgSendSuper(& superClass, sel_registerName("send"));
     //带参数
     objc_msgSendSuper(& superClass, sel_registerName("send:"),@"Hello World");
    

消息转发的流程:

拦截异常有三次机会:

(1)动态转发

+(BOOL)resolveInstanceMethod:(SEL)sel;

如果当前对象调用了一个不存在的方法,Runtime会调用resolveInstanceMethod:来进行动态方法解析, 我们需要用class_addMethod函数完成向特定类添加特定方法实现的操作,返回false,完成方法替换,结束消息转发,返回true,则进入下一步forwardingTargetForSelector:重定向.

+(BOOL)resolveInstanceMethod:(SEL)sel {
 NSString *selector = NSStringFromSelector(sel);
 Class class = [self class];
 if ([selector isEqualToString:@"sendMessage"]) {
   class_addMethod(class, sel, (IMP)sendMessage, "v@:");
   return false;
}
 return true;
}
  • 签名符号含义:
c   代表char类型
i   代表int类型
s   代表short类型
l   代表long类型,在64位处理器上也是按照32位处理
q   代表long long类型
C   代表unsigned char类型
I   代表unsigned int类型
S   代表unsigned short类型
L   代表unsigned long类型
Q   代表unsigned long long类型
f   代表float类型
d   代表double类型
B   代表C++中的bool或者C99中的_Bool
v   代表void类型
*   代表char *类型
@   代表对象类型
#   代表类对象 (Class)
:   代表方法selector (SEL)
[array type]      代表array
{name=type...}  代表结构体
(name=type...)  代表union
bnum    A bit field of num bits
^type   A pointer to type
 ?  An unknown type (among other things, this code is used for function pointers)

(2)快速转发

-(id)forwardingTargetForSelector:(SEL)aSelector;

如果步骤1没有实现拦截,返回true表示继续消息转发,在消息转发机制执行前,Runtime 系统会再给我们一次重定向的机会,通过重载forwardingTargetForSelector:方法来替换消息的接受者为其他对象,返回有值,结束转发。返回nil,则进步下一步forwardInvocation:

- (id)forwardingTargetForSelector:(SEL)aSelector {
NSString *selector = NSStringFromSelector(aSelector);

Class class = [self class];
if ([selector isEqualToString:@"sendMessage"]) {
    class_addMethod(class, aSelector, (IMP)sendMessage, "v@");
    return [Person1 new];
}

return [super forwardingTargetForSelector:aSelector];}

如果步骤1、步骤2都没有完成消息转发,则还有最后一条保命大法。

(3)慢速转发

此方法会经历两个过程,签名,消息转发

获取方法签名进入下一步,进行消息转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
//进行消息转发
 - (void)forwardInvocation:(NSInvocation *)anInvocation;

1.签名

-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSString *sel =NSStringFromSelector(aSelector);
if([sel isEqualToString:@"sendMessage"]){
  return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}

2.通常forwardInvocation:,会用一个替代类,并且能响应异常方法的对象进行拦截,

- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = anInvocation.selector;
BLFather *father = [BLFather new];
if ([father respondsToSelector:sel])    {
    [anInvocation invokeWithTarget:father];
   }
}

(4)消息无法响应

如果以上方法都找不到会执行
此方法也就是会发生崩溃的

- (void)doesNotRecognizeSelector:(SEL)aSelector;

方法找不到:

- (void)doesNotRecognizeSelector:(SEL)aSelector{
  NSString *sel=NSStringFromSelector(aSelector);
   NSLog(@"%@",sel);
}

以上就是消息转发的全部过程,仅供参考

相关文章

网友评论

      本文标题:消息转发 :objc_msgSend

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