消息
在Objective-C中,消息是直到运行的时候才和方法实现绑定的。消息机制的关键在于编译器为类和对象生成的结构。每个类的结构中至少包括两个基本元素:
(1) 指向父类的指针
(2)类的方法表。方法表将方法选标和该类的方法实现的地址关联起来。
消息框架如下
消息转发
当向someObject发送消息,但runtime system在当前类和父类中都找不到对应的方法实现时。runtime system并不会立即报错使程序崩溃,而是依次执行下列步骤:
分别简述一下流程:
(1) 动态方法解析 向当前类发送 resolveInstanceMethod: 或者 resolveClassMethod: 消息,检查是否动态向该类添加了方法。
(2)快速消息转发 检查该类是否实现了forwardingTargetForSelector: 方法,若实现了则调用该方法。若该方法返回值对象非nil或非self,则向该返回对象重新发送消息。
(3)标准消息转发 runtime发送methodSignatureForSelector:方法获取Selector对应的方法签名。返回值非nil则通过forwardInvocation:转发消息,返回值为nil则向当前对象发送doesNotRecognizeSelector:消息,程序崩溃退出。
示例
示例界面如下
#pragma mark - event response
- (void)dynamicBtnClicked:(id)sender
{
self.dianDi86.name = @"DianDi86";
NSLog(@"%@",self.dianDi86.name);
}
- (void)fastForwardBtnClicked:(id)sender
{
[self.whiteHouse performSelector:@selector(setSex:) withObject:@"Boy"];
}
- (void)normalForwardBtnClicked:(id)sender
{
[self.audioA4L performSelector:@selector(setSex:) withObject:@"Boy"];
}
动态方法解析
#import "People.h"
#include <objc/runtime.h>
void dynamicSetNameIMP(id self, SEL _cmd, NSString *name)
{
NSLog(@"%@ %@",self, NSStringFromSelector(_cmd));
if (((People*)self)->_name != name) {
((People*)self)->_name = [name copy];
}
}
NSString* dynamicNameIMP(id self, SEL _cmd)
{
NSLog(@"%@ %@",self, NSStringFromSelector(_cmd));
return ((People*)self)->_name;
}
@implementation People
@dynamic name;
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(setName:)) {
class_addMethod(self, sel, (IMP)dynamicSetNameIMP, "v@:@");
return YES;
} else if (sel == @selector(name)) {
class_addMethod(self, sel, (IMP)dynamicNameIMP, "@@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
点击示例界面中的动态方法解析console log 如下
快速消息转发
- (id)forwardingTargetForSelector:(SEL)aSelector
{
// House 类需要将消息转发给People
NSLog(@"%@ %@",self, NSStringFromSelector(_cmd));
if ([self.master respondsToSelector:aSelector]) {
return self.master;
}
return nil;
}
点击示例界面中的快速消息转发console log 如下
标准消息转发
- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector
{
NSLog(@"%@ %@",self, NSStringFromSelector(_cmd));
NSMethodSignature *sign = [super methodSignatureForSelector:aSelector];
if (sign == nil) {
sign = [self.master methodSignatureForSelector:aSelector];
}
return sign;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"%@ %@",self, NSStringFromSelector(_cmd));
SEL selector = [anInvocation selector];
if ([self.master respondsToSelector:selector]) {
[anInvocation invokeWithTarget:self.master];
}
}
点击示例界面中的快速消息转发console log 如下