Runtime
2020-08-08 本文已影响0人
KeepOnline
面试题
1.讲一下OC的消息机制
- OC的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发一条(selector方法名)
- objc_msgSend底层有3大阶段
- 消息发送(当前类、父类中查找)、动态方法解析、消息转发
2.什么是Runtime?平时项目中有用过吗?
- OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
- OC的动态性就是有Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数
- 平时编写的OC代码,底层都是转换成了Runtime API进行调用
- 具体应用
- 利用关联对象(AssociatedObject)给分类添加属性
- 遍历类的所有成员变量(修改textfield的占位文字颜色(_placeholderLabel.textColor)、字典转模型(利用Runtime遍历所有成员的属性或者成员变量,利用KVC设值)、自动归档解档)
- 方法交换实现(交换系统的方法)
- class_replaceMethod method_exchangeImplementations
- 利用消息转发机制解决方法找不到的异常问题
- ......
3.打印结果分别是什么?
@interface KPLStudent : KPLPerson
@end
@implementation KPLStudent
- (instancetype)init {
if (self = [super init]) {
NSLog(@"[self class] = %@", [self class]);
NSLog(@"[super class] = %@", [super class]);
NSLog(@"[self superclass] = %@", [self superclass]);
NSLog(@"[super superclass] = %@", [super superclass]);
}
return self;
}
@end
@interface KPLPerson : NSObject
@end
BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [[KPLPerson class] isKindOfClass:[KPLPerson class]];
BOOL res4 = [[KPLPerson class] isMemberOfClass:[KPLPerson class]];
NSLog(@"%d %d %d %d", res1, res2, res3, res4);
4.以下代码能不能执行成功?如果可以,打印结果是什么?
@interface KPLPerson : NSObject
@propety (copy, nonatomic) NSString *name;
@end
@implementation
- (void)print {
NSLog(@"my name's %@", self.name);
}
@end
@implementation
- (void)viewDidLoad {
[super viewDidLoad];
id cls = [KPLPerson class];
void *obj = &cls;
[(__bridge id)obj print];
}
@end
- Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同
- Objective-C的动态性是由Runtime API来支撑的
- Runtime API提供的接口基本都是c语言的,源码有C\C++汇编语言编写
objc_msgSend执行流程01-消息发送
- receiver是否为nil
- 是(退出)
- 否
- 从receiverClass的cache中查找方法
- 找到了方法(调用方法结束查找)
- 没有找到方法
- 从receiverClass的class_rw_t中查找方法
- 找到了方法(调用方法,结束查找,并将方法缓存到receiverClass的cache中)
- 没有找到方法
- a.从superClass的cache中查找方法
- a.找到了方法(调用方法,结束查找,并将方法缓存到receiverClass的cache中)
- a.没有找到方法(从superClass的class_rw_t中查找方法
- a.找到了方法(调用方法,结束查找,并将方法缓存到receiverClass的cache中)
- 没有找到方法(上层是否还有superClass)
- 有superClass,执行a
- 没有superClass(动态方法解析)
- 有superClass,执行a
- a.从superClass的cache中查找方法
- 从receiverClass的class_rw_t中查找方法
- 从receiverClass的cache中查找方法
- 如果是从class_rw_t中查找方法
- 已经排序的,二分查找
- 没有排序的,遍历查找
- receiver通过isa指针找到receiverClass
- receiver通过superclass指针找到superClass
objc_msgSend执行流程02-动态方法解析
- 是否曾经有动态解析
- 是(消息转发)
- 否
- 调用+resolveInstanceMethod:或者+resolveClassMethod:方法来动态解析方法
- 标记为已经动态解析
- 消息发送
- 标记为已经动态解析
- 调用+resolveInstanceMethod:或者+resolveClassMethod:方法来动态解析方法
-
开发者可以实现以下方法,来动态添加方法实现
- +resolveInstanceMethos:
- +resolveClassMethod:
-
动态解析过后,会重新走"消息发送"的流程
- "从receiverClass的cache中查找方法"这一步开始执行
动态添加方法
+ (BOOL)resolveInstanceMethos:(SEL)sel {
if (sel == @selector(test)) {
Method method = class_getInstanceMethod(self, @selector(other));
class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
return YES;
}
return [super resolveInstanceMethod:sel];
}
void other(id self, SEL _cmd) {
NSLog(@"%@-%s--%s", self, sel_getName(_cmd), __func__);
}
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(test)) {
class_addMethod(self, sel, (IMP)other, @"v@:");
return YES;
}
return [super resolveClassMethod:sel];
}
// Method可以理解为等价于struct method_t *
@interface KPLPersom : NSObject
@property (assign, nonatomic) int age;
@end
@implementation KPLPerson
@dynamic age;
@end
// @dynamic是告诉编译器不用自动生成getter和setter的实现,等到运行时再添加方法实现
objc_msgSend的执行流程03-消息转发
-
调用forwardingTargetForSelector:方法
- 返回值不为nil(objc_msgSend(返回值,SEL)
- 返回值为nil
- 调用methodSignatureForSelector:方法
- 方绘制不为nil(调用forwardInvocation:方法
- 返回值为nil(调用doesNotRecognizeSelector:方法)
- 调用methodSignatureForSelector:方法
-
开发者可以再forwardInvocation:方法中自定义任何逻辑
-
以上方法都有对象方法、类方法2个版本(前面可以使加号+,也可以是减号-)
生成NSMethodSignature
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCType:"i@:i"];
NSMethodSignature *signature = [[[KPLPerson alloc] init] methodSignatureForSelector:@selecote(test:)];
super的本质
- super调用,底层会转换为objc_msgSendSuper2函数的调用,接收2个参数
- struct objc_super2
- SEL
struct objc_super2 {
id receiver;
Class current_class;
};
- reveiver是消息接收者
- current_class是receiver的Class对象