Message Forwarding + 动态绑定
> “unrecognized selector sent to instance …" 错误
动态绑定:objc_msgSend的实现,查找implementation表,最终是C函数形式的调用。
当某个类(以及继承树上的父类)编译时没有动态实现被调用的method的时候,一般情况下会被NSObject处理这个method(selector)并抛出异常。
我们有两种常用(三种)方法来动态的补充使得相应的lei可以处理此selector。
一种是动态方法手段(Dynamic Method Resolution),第二种是委托其他对象处理(Replacement Receiver)。下边通过例子直接说明。
============================================= NormalObject.h @interface NormalObject : NSObject
- (void)showMe;
// 实际上没有显式实现这个selector
- (void)testMe; @end
============================================== #import "NormalObject.h"
#import "ReplacementObject.h"
#import <objc/runtime.h>
#define kUseDynamicMethod YES
@interface NormalObject()
@property (strong, nonatomic) ReplacementObject *replacement;
@end
@implementation NormalObject
- (instancetype)init
{
self = [super init];
if (self) {
self.replacement = [[ReplacementObject alloc] init];
}
return self;
}
#pragma mark - Dynamic Method Resolution // 当且仅当未显示实现的selector被调用时,这里会被调用。- 再次调用同样的selector,这里不会被执行,因为已经cached了 + (BOOL)resolveInstanceMethod:(SEL)sel{
if (kUseDynamicMethod) {
NSString *selName = NSStringFromSelector(sel); NSLog(@"required for: %@", selName); // 使用class_addMethod动态加入method,此后每次对testMe的调用都会直接调用impAddedMethod class_addMethod(self, sel, (IMP)impAddedMethod, "v@:");
return YES;
} else {
return NO;
}
}
// must be a C method
void impAddedMethod(id self, SEL _sel){
NSLog(@"You guy send an unknown selector!!!");
}
#pragma mark - Replacement Receiver
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"%s called", __FUNCTION__);
return self.replacement;
}
- (void)showMe{
NSLog(@"NormalObject showMe called");
} @end ============================================== 辅助类: @interface ReplacementObject : NSObject
- (void)testMe; @end
@implementation ReplacementObject
- (void)testMe{
NSLog(@"[ReplacementObject] Wahahahah, i implement this!");
} @end ============================================== 调用
[obj1 showMe]; // 一般情况 [obj1 testMe]; // testMe没用命中,通过resolveInstanceMethod或forwardingTargetForSelector寻找解决方案,没有找到的话由NSObject来抛异常 NSLog(@"call it again"); [obj1 testMe]; // 第二次调用,resolveInstanceMethod不会再次触发,因为已经cached了。forwardingTargetForSelector则会每次都执行,因此Dynamic Method的效率会更高。
两者使用场景不同。Dynamic Method适用于@dynamic修饰的property的setter和getter的实现。Replacement Receiver适用于用composition模式来达到多重继承的效果。 阅读更多
当某个类(以及继承树上的父类)编译时没有动态实现被调用的method的时候,一般情况下会被NSObject处理这个method(selector)并抛出异常。
我们有两种常用(三种)方法来动态的补充使得相应的lei可以处理此selector。
一种是动态方法手段(Dynamic Method Resolution),第二种是委托其他对象处理(Replacement Receiver)。下边通过例子直接说明。
============================================= NormalObject.h @interface NormalObject : NSObject
- (void)showMe;
// 实际上没有显式实现这个selector
- (void)testMe; @end
============================================== #import "NormalObject.h"
#import "ReplacementObject.h"
#import <objc/runtime.h>
#define kUseDynamicMethod YES
@interface NormalObject()
@property (strong, nonatomic) ReplacementObject *replacement;
@end
@implementation NormalObject
- (instancetype)init
{
self = [super init];
if (self) {
self.replacement = [[ReplacementObject alloc] init];
}
return self;
}
#pragma mark - Dynamic Method Resolution // 当且仅当未显示实现的selector被调用时,这里会被调用。- 再次调用同样的selector,这里不会被执行,因为已经cached了 + (BOOL)resolveInstanceMethod:(SEL)sel{
if (kUseDynamicMethod) {
NSString *selName = NSStringFromSelector(sel); NSLog(@"required for: %@", selName); // 使用class_addMethod动态加入method,此后每次对testMe的调用都会直接调用impAddedMethod class_addMethod(self, sel, (IMP)impAddedMethod, "v@:");
return YES;
} else {
return NO;
}
}
// must be a C method
void impAddedMethod(id self, SEL _sel){
NSLog(@"You guy send an unknown selector!!!");
}
#pragma mark - Replacement Receiver
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"%s called", __FUNCTION__);
return self.replacement;
}
- (void)showMe{
NSLog(@"NormalObject showMe called");
} @end ============================================== 辅助类: @interface ReplacementObject : NSObject
- (void)testMe; @end
@implementation ReplacementObject
- (void)testMe{
NSLog(@"[ReplacementObject] Wahahahah, i implement this!");
} @end ============================================== 调用
[obj1 showMe]; // 一般情况 [obj1 testMe]; // testMe没用命中,通过resolveInstanceMethod或forwardingTargetForSelector寻找解决方案,没有找到的话由NSObject来抛异常 NSLog(@"call it again"); [obj1 testMe]; // 第二次调用,resolveInstanceMethod不会再次触发,因为已经cached了。forwardingTargetForSelector则会每次都执行,因此Dynamic Method的效率会更高。
两者使用场景不同。Dynamic Method适用于@dynamic修饰的property的setter和getter的实现。Replacement Receiver适用于用composition模式来达到多重继承的效果。 阅读更多
声明:该文观点仅代表作者本人,入门客AI创业平台信息发布平台仅提供信息存储空间服务,如有疑问请联系rumenke@qq.com。
- 上一篇:没有了
- 下一篇:没有了