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);
}
网友评论