面试题答案
一键面试1. 消息转发机制对比
- Objective - C:采用动态运行时机制,在编译期并不确定调用哪个方法,运行时通过 isa 指针找到对象所属类的方法列表,若找不到则启动消息转发流程。消息以 SEL(方法选择器)为标识,可向任何对象发送任何消息。
- Java:采用静态绑定机制,在编译期就确定了调用的方法,对象只能调用在其类或父类中声明的方法。若调用不存在的方法,编译时就会报错。
- C++:默认也是静态绑定,通过函数名和参数列表确定调用的函数。但 C++ 支持虚函数和多态,在运行时根据对象的实际类型决定调用哪个虚函数,但这种机制和 Objective - C 的消息转发仍有很大区别,C++ 不能向对象发送未声明的函数调用。
2. Objective - C 消息转发机制优势
- 灵活性:
- 可以向对象发送在编译期未知的消息,使得代码更具动态性。例如在 iOS 的
UIWebView
与 JavaScript 交互中,UIWebView
可以通过消息转发机制处理来自 JavaScript 的各种自定义消息,而无需在编译期明确知晓这些消息。 - 支持多继承的某些特性,通过分类(Category)和协议(Protocol)结合消息转发,可以让一个类表现出多种不同的行为,仿佛实现了多继承。
- 可以向对象发送在编译期未知的消息,使得代码更具动态性。例如在 iOS 的
- 运行效率:虽然动态消息转发在运行时会有额外开销,如查找方法列表、动态解析等,但 Objective - C 的运行时系统经过优化,在实际应用中对于频繁调用的方法,其缓存机制可以大幅提高效率。例如,一个视图控制器类中经常调用的
viewDidLoad
方法,首次调用后会被缓存,后续调用时直接从缓存获取,减少查找开销。 - 代码维护性:
- 降低模块间的耦合度。当需要为某个类添加新功能时,无需直接修改类的代码,通过消息转发可以在外部实现相关功能。比如为系统类
NSString
添加一个新的处理方法,不需要子类化NSString
,而是通过分类和消息转发机制在分类中实现,使得原类的代码保持干净,易于维护。 - 方便实现功能扩展和替换。在项目后期,如果需要替换某个方法的实现逻辑,通过消息转发机制可以在不影响原有调用代码的情况下,在转发流程中改变实现,提高代码的可维护性和扩展性。
- 降低模块间的耦合度。当需要为某个类添加新功能时,无需直接修改类的代码,通过消息转发可以在外部实现相关功能。比如为系统类
3. 实际项目中利用优势的示例
假设在一个 iOS 电商项目中,有一个商品展示类 ProductViewController
。
- 灵活性应用:在商品详情页,需要根据不同的用户等级显示不同的操作按钮。通过消息转发机制,可以在运行时根据用户等级动态决定向
ProductViewController
发送不同的消息来显示对应按钮。例如,普通用户可能收到showNormalUserActions
消息,高级用户收到showVIPUserActions
消息,而这些消息可以在运行时根据用户等级动态发送,无需在编译期确定。
// 假设 User 类有一个属性表示用户等级
User *currentUser = [User currentUser];
if (currentUser.userLevel == UserLevelNormal) {
[productViewController performSelector:@selector(showNormalUserActions)];
} else if (currentUser.userLevel == UserLevelVIP) {
[productViewController performSelector:@selector(showVIPUserActions)];
}
- 代码维护性应用:在项目迭代过程中,发现商品价格显示逻辑需要调整。原
ProductViewController
中有一个showPrice
方法用于显示价格。通过消息转发机制,可以在分类中重新实现消息转发逻辑,修改价格显示方式,而不影响其他地方对showPrice
方法的调用。
// 在 ProductViewController+PriceFix 分类中
@implementation ProductViewController (PriceFix)
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL originalSelector = anInvocation.selector;
if (originalSelector == @selector(showPrice)) {
// 新的价格显示逻辑
NSLog(@"New price display logic");
} else {
[self.nextResponder forwardInvocation:anInvocation];
}
}
@end