面试题答案
一键面试Objective-C运行时动态添加方法的原理
- 消息发送机制:在Objective-C中,当向一个对象发送消息时,运行时系统会根据对象的isa指针找到它的类,然后在类的方法列表中查找对应的方法。如果在本类中没有找到,会沿着继承体系向父类查找。
- 动态方法解析:如果在方法列表中没有找到对应的方法,运行时会进入动态方法解析阶段。在这个阶段,运行时会给类一次机会,尝试动态添加方法来处理这个消息。具体来说,会调用
+ (BOOL)resolveInstanceMethod:(SEL)sel
(针对实例方法) 或+ (BOOL)resolveClassMethod:(SEL)sel
(针对类方法)。如果在这个方法中动态添加了方法,运行时会重新进行消息发送流程,这次就可以找到并调用新添加的方法。
使用动态添加方法的情况
- 懒加载方法:某些方法只有在特定条件下才需要实现,为了减少初始加载时的资源消耗,可以在运行时动态添加。
- 插件化架构:在插件化开发中,插件的功能可能在运行时才确定,通过动态添加方法可以实现插件与主程序的灵活交互。
- 代码解耦:将一些不常用或可能变化的功能通过动态添加方法的方式实现,避免在编译时就将所有代码耦合在一起。
实现思路
- 定义方法签名:首先需要定义动态添加方法的函数原型,确定其参数和返回值类型。
- 实现动态添加方法的逻辑:在
+ (BOOL)resolveInstanceMethod:(SEL)sel
或+ (BOOL)resolveClassMethod:(SEL)sel
方法中,使用运行时函数class_addMethod
来动态添加方法。 - 调用动态添加的方法:在需要调用动态添加方法的地方,向对象发送相应的消息,运行时会在动态方法解析阶段处理这个消息。
关键代码片段
- 定义动态添加的方法
// 动态添加的实例方法实现
void dynamicMethodIMP(id self, SEL _cmd, NSString *param) {
NSLog(@"动态添加的方法被调用,参数: %@", param);
}
// 动态添加类方法实现
void dynamicClassMethodIMP(id self, SEL _cmd, NSString *param) {
NSLog(@"动态添加的类方法被调用,参数: %@", param);
}
- 动态添加实例方法
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(dynamicInstanceMethod:)) {
class_addMethod(self, sel, (IMP)dynamicMethodIMP, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
- 调用动态添加的实例方法
MyClass *obj = [[MyClass alloc] init];
if ([obj respondsToSelector:@selector(dynamicInstanceMethod:)]) {
[obj performSelector:@selector(dynamicInstanceMethod:) withObject:@"Hello"];
}
- 动态添加类方法
@implementation MyClass
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(dynamicClassMethod:)) {
class_addMethod(object_getClass(self), sel, (IMP)dynamicClassMethodIMP, "v@:@");
return YES;
}
return [super resolveClassMethod:sel];
}
@end
- 调用动态添加的类方法
if ([MyClass respondsToSelector:@selector(dynamicClassMethod:)]) {
[MyClass performSelector:@selector(dynamicClassMethod:) withObject:@"World"];
}
上述代码中,dynamicMethodIMP
和dynamicClassMethodIMP
是动态添加方法的具体实现。在resolveInstanceMethod:
和 resolveClassMethod:
方法中,根据SEL
判断是否是需要动态添加的方法,如果是则使用class_addMethod
添加方法。最后在调用时,通过respondsToSelector:
检查对象或类是否能响应动态添加的方法,然后使用performSelector:
调用。