上面这段异常信息是由NSObject的“doseNotRecognizeSelector:” 方法所抛出的,此异常表明:消息接受者的类型是LJBDictionary, 而该接受者无法理解名为setStr的方法。
消息转发分为2大阶段。第一阶段先征询接受者,所属的类,看是否能动态添加方法,以处理当前这个“未知的选择子”(unknown selector),这叫做“动态方法解析(dynamic method resolution)”。第二阶段涉及“完整的消息转发机制(full forwarding mechanism)”。如果运行期系统已经把第一阶段执行完了,那么接受者自己就无法再以动态新增方法的手段来响应包含该选择子的消息了。此时,运行期系统会请求接受者以其他手段来处理与消息相关的方法调用。这又细分为二小步。首先,请接受者看看有没有其他对象能处理这条消息。若有,则运行期系统又会把消息转给那个对象,于是消息转发过程结束,一切正常。若没有“备援的接受者”(replacement receiver),则启动完整的消息转发机制,运行期系统会把与消息有关的全部细节都封装到NSInvocation对象中,再给接受者最后一次机会,令其设法解决当前还未处理的这条消息。
动态方法解析
对象在收到无法解读的消息后,首先将调用其所属类的下列类方法:
+ (BOOL)resolveInstanceMethod:(SEL)sel;
该方法的参数就是那个未知的选择子,其返回值为Boolean类型,表示这个类是否能新增一个实例方法用以处理此选择子。在继续往下执行转发机制之前,本类有机会新增一个处理此选择子的方法。假如尚未实现的方法不是实例方法而是类方法,那么运行期系统就会调用另外一个方法"resolveClassMethod:"。
使用这种办法的前提是:相关发放的实现代码已经写好,只等着运行的时候动态插在类里面就可以了。此方案常用来实现@dynamic属性。(详情见最后的demo)
备援接受者
完整的消息转发
消息转发全流程
消息转发以完整的例子实现动态方法解析
下面示范如何以动态方法解析来实现@dynamic属性。将属性声明为@dynamic,编译器就不会为其自动生成实例变量及存取方法了。
在LJBDictionary.h文件中
#import <Foundation/Foundation.h>
@interface LJBDictionary : NSObject
@property(nonatomic, copy) NSString *str;
@end
在LJBDictionary.m文件中
#import "LJBDictionary.h"
#import <objc/runtime.h>
@interface LJBDictionary ()
@property(nonatomic, strong) NSMutableDictionary *backingStore;
@end
@implementation LJBDictionary
@dynamic str;
- (instancetype)init
{
self = [super init];
if (self) {
_backingStore = [[NSMutableDictionary alloc]init];
}
return self;
}
+(BOOL)resolveInstanceMethod:(SEL)sel{
NSString *selectorString = NSStringFromSelector(sel);
if ([selectorString hasPrefix:@"set"]) {
class_addMethod(self, sel, (IMP)ljbDictionarySetter, "v@:@");
} else {
class_addMethod(self, sel, (IMP)ljbDictionaryGetter, "@@:");
}
return YES;
}
//getter函数
id ljbDictionaryGetter(id self, SEL _cmd) {
LJBDictionary *typedSelf = (LJBDictionary *)self;
NSMutableDictionary *backingStoe = typedSelf.backingStore;
NSString *key = NSStringFromSelector(_cmd);
return [backingStoe valueForKey:key];
}
//setter函数
void ljbDictionarySetter(id self, SEL _cmd, id value) {
LJBDictionary *typedSelf = (LJBDictionary *)self;
NSMutableDictionary *backingStore = typedSelf.backingStore;
NSString *selectorString = NSStringFromSelector(_cmd);
NSMutableString *key = [selectorString mutableCopy];
//删除“ : ”
[key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)];
//删除“set“
[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];
}
}
@end
在主控制器ViewController.m中:
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
LJBDictionary *dict = [[LJBDictionary alloc]init];
dict.str = @"lijiangbo";
NSLog(@"%@",dict.str);
}
@end
打印结果
2017-04-07 21:37:54.999 test[4420:8019277] lijiangbo
网友评论