面试题答案
一键面试动态方法解析
- 类方法解析:当对象接收到无法识别的消息时,运行时系统首先会调用类方法
+ (BOOL)resolveClassMethod:(SEL)sel
。如果该类实现了此方法,可以在这个方法中动态添加类方法的实现。例如:
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(aClassMethod)) {
class_addMethod(self, sel, (IMP)aClassMethodImplementation, "v@:");
return YES;
}
return [super resolveClassMethod:sel];
}
- 实例方法解析:如果类方法解析未处理该消息,运行时会调用实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
。同样,可以在这个方法中动态添加实例方法的实现。例如:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(anInstanceMethod)) {
class_addMethod(self, sel, (IMP)anInstanceMethodImplementation, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
备用接收者
如果动态方法解析没有处理该消息,运行时会尝试寻找备用接收者。它会调用 -(id)forwardingTargetForSelector:(SEL)aSelector
方法。在这个方法中,可以返回一个能够响应这个消息的对象。例如:
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([otherObject respondsToSelector:aSelector]) {
return otherObject;
}
return nil;
}
完整的消息转发流程
- 动态方法解析:运行时首先尝试动态解析方法,调用
+ (BOOL)resolveClassMethod:(SEL)sel
或+ (BOOL)resolveInstanceMethod:(SEL)sel
,看是否能动态添加方法实现。 - 备用接收者:如果动态方法解析未处理,运行时调用
-(id)forwardingTargetForSelector:(SEL)aSelector
寻找备用接收者。 - 完整转发:如果前两步都未处理,进入完整转发阶段。首先调用
-(void)forwardInvocation:(NSInvocation *)anInvocation
,在这个方法中,可以重新设置anInvocation
的目标对象,让其指向能处理该消息的对象,然后调用[anInvocation invoke]
来执行消息。如果forwardInvocation:
也未处理,最后会调用-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
来获取方法签名,如果获取到签名,会再次调用forwardInvocation:
,若未获取到签名,会抛出unrecognized selector
异常。
应用场景
- 框架设计:在设计框架时,某些功能可能在运行时才确定具体实现,通过反射机制可以实现灵活的功能扩展。例如,一个网络请求框架,可能在运行时根据不同的服务器响应决定调用不同的解析方法,通过动态方法解析可以在运行时添加相应的解析方法。
- 代码简洁性:在一些情况下,多个对象可能有相似的功能但并不属于同一个继承体系。通过备用接收者和消息转发,可以避免重复代码。比如,在一个复杂的用户界面中,不同的视图控制器可能有一些相同的辅助功能,通过消息转发可以将这些功能集中到一个辅助对象中处理。
优势
- 灵活性:使得程序在运行时能够根据实际情况动态调整行为,而不需要在编译时就确定所有的方法调用,提高了代码的灵活性和可扩展性。
- 代码复用:备用接收者和消息转发机制可以避免在不同类中重复实现相同功能,提高了代码的复用性,减少了代码冗余。
- 优雅的接口设计:在框架设计中,反射机制有助于提供简洁、易用的接口,让开发者可以在运行时灵活地定制功能,而不需要修改框架的核心代码。