- 动态方法解析阶段
- 当向对象发送一个未实现的实例方法时,运行时首先会调用
+ (BOOL)resolveInstanceMethod:(SEL)sel
类方法,这个方法属于类对象。如果是类方法,则调用+ (BOOL)resolveClassMethod:(SEL)sel
。
- 在
resolveInstanceMethod:
中,可以通过class_addMethod
函数动态添加方法实现。例如:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(yourUnimplementedMethod)) {
IMP imp = imp_implementationWithBlock(^(id self, NSString *param) {
// 方法实现代码
});
class_addMethod(self, sel, imp, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
- 这里涉及到的关键数据结构是类对象(
Class
),它存储了类的相关信息,包括方法列表等。类对象在运行时由objc_class
结构体表示,其定义大致如下:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if!__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
class_addMethod
函数用于向类中添加方法,其原型为:
BOOL class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types);
IMP
是函数指针类型,指向方法的具体实现;SEL
是方法选择器,用于唯一标识一个方法;types
是方法的编码字符串,描述了方法的参数和返回值类型。
- 备用接收者阶段
- 如果动态方法解析没有处理该方法,运行时会进入备用接收者阶段,调用
- (id)forwardingTargetForSelector:(SEL)aSelector
方法。
- 在这个方法中,可以返回一个能够处理该消息的对象。例如:
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(yourUnimplementedMethod)) {
return anotherObjectThatCanHandleTheMethod;
}
return nil;
}
- 完整的消息转发阶段
- 如果备用接收者也没有找到,运行时会进入完整的消息转发阶段。首先调用
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
方法。
- 该方法需要返回一个
NSMethodSignature
对象,用于描述未实现方法的参数和返回值类型。例如:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(yourUnimplementedMethod)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
- 然后调用
- (void)forwardInvocation:(NSInvocation *)anInvocation
方法,在这个方法中,可以手动处理消息转发,比如调用其他对象的相应方法。例如:
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = anInvocation.selector;
if ([anotherObjectThatCanHandleTheMethod respondsToSelector:sel]) {
[anInvocation invokeWithTarget:anotherObjectThatCanHandleTheMethod];
} else {
[super forwardInvocation:anInvocation];
}
}
- 如果在
forwardInvocation:
中没有处理该消息,最终会调用- (void)doesNotRecognizeSelector:(SEL)aSelector
方法,抛出NSInvalidArgumentException
异常,提示该对象无法识别该选择器。