1.在说消息转发前需要先理解objc_magsent, objc_msgSend函数会依据接收者与选择子(SEL)的类型来调整适当的方法。为了完成此操作,该方法需要在接收者所属的类中搜寻其“方法列表”如果能找到与选择子名称相符的方法,就调整至其代码,如果找不到,就沿着继承体系继续向上查找,等找到合适的方法之后在跳转,如果还是找不到相符合的方法那就执行消息转发
2.上面是消息转发的条件消息转发包括一下:
1.动态方法解析(+ (BOOL)resolveInstanceMethod:(SEL)selector)他的作用是 类是否新增实力方法处理选择子有返回yes没有No
2.备用接收者 (- (id)forwardingTargetForSelector:(SEL)aSelector) 如果1返回类NO就到2,2的作用是把消息转给其他接收者来处理消息 没有就返回nil
3.完整消息转发 (-(void)forwardInvocation:(NSInvocation *)anInvocation) 如果2返回nil 创建NSInvocation 对象 封装全部细节 消息派发系统把消息派给目标对象
举个例子 模拟下字典 为属性添加set get 方法
@interface EOCAutoDictionary : NSObject
@property(nonatomic,strong) NSString *string;
@property(nonatomic,strong) NSNumber *number;
@property(nonatomic,strong) NSDate *date;
@property(nonatomic,strong) id opaqueObject;
@end
#import "EOCAutoDictionary.h"
#import <objc/runtime.h>
@interface EOCAutoDictionary()
@property(nonatomic,strong) NSMutableDictionary *backingStore;
@end
@implementation EOCAutoDictionary
@dynamic string,number,date,opaqueObject;//这个是让用户自己实现get set 方法
- (id)init{
if (self = [super init]) {
_backingStore = [NSMutableDictionary new];
}
return self;
}
+ (BOOL)resolveInstanceMethod:(SEL)selector{
NSString *selectorString = NSStringFromSelector(selector);
if ([selectorString hasPrefix:@"set"]) {
class_addMethod(self,
selector,
(IMP)autoDictionarySetter,
"v@:@");
}else{
class_addMethod(self,
selector,
(IMP)autoDictionaryGetter,
"@@:");
}
return YES;
}
id autoDictionaryGetter(id self ,SEL _cmd){
EOCAutoDictionary *typedSelf = (EOCAutoDictionary *)self;
NSMutableDictionary *backingStore = typedSelf.backingStore;
NSString *key = NSStringFromSelector(_cmd);
return [backingStore objectForKey:key];
}
void autoDictionarySetter(id self ,SEL _cmd,id value){
EOCAutoDictionary *typedSelf = (EOCAutoDictionary *)self;
NSMutableDictionary *backingStore = typedSelf.backingStore;
NSString *selectorString = NSStringFromSelector(_cmd);
NSMutableString *key = [selectorString mutableCopy];
[key deleteCharactersInRange:NSMakeRange(key.length-1, 1)];
[key deleteCharactersInRange:NSMakeRange(0, 3)];
NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString];
[key replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstChar];
if (value) {
[backingStore setObject:value forKey:key];
}else{
[backingStore removeObjectForKey:key];
}
}
外面调用
EOCAutoDictionary *dict = [EOCAutoDictionary new];
dict.date = [NSDate dateWithTimeIntervalSince1970:475372800];
NSLog(@"date = %@",dict.date);
网友评论