Runtime之类对象、元类对象、消息传递
2019-06-19 本文已影响0人
Jimmy_L_Wang
类对象与元类对象的关系
- 类对象存储实例方法列表等信息。
- 元类对象存储类方法列表等信息。
消息传递
OBJC_EXPORT void
objc_msgSend(void /* id self, SEL op, ... */ )
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
msg_send01.png
OBJC_EXPORT void
objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver; //接收者
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
};
msg_send02.png
消息传递机制
msg_send03.png缓存查找
给定值是SEL
,目标值是对应bucket_t
中的IMP
当前类中查找
- 对于已排序好的列表,采用二分查找算法查找方法对应执行函数;
- 对于没有排序的列表,采用一般遍历查找方法对应执行函数;
父类逐级查找
父类逐级查找.png消息转发
消息转发.png+ (BOOL) resolveInstanceMethod:(SEL)aSEL //动态地为实例方法的给定选择器提供实现
{
if (aSEL == @selector(resolveThisMethodDynamically))
{
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSel];
}
在调用Objective-C转发机制之前调用此方法。如果
respondsToSelector:
或instancesRespondToSelector:·
被调用,则动态方法解析器有机会首先为给定的选择器提供IMP
//返回首先应将无法识别的消息定向到的对象。
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSLog(@"forwardingTargetForSelector:");
return nil;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
NSLog(@"methodSignatureForSelector:");
// v 代表返回值是void类型的 @代表第一个参数类型是id,即self
// : 代表第二个参数是SEL类型的 即@selector(test)
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
else{
return [super methodSignatureForSelector:aSelector];
}
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"forwardInvocation:");
}
Method-Swizzling
method_swizzling.png#import "MethodChange.h"
#import <objc/runtime.h>
@implementation MethodChange
+ (void)load
{
//获取before方法
Method before = class_getInstanceMethod(self, @selector(methodChangeBefore));
//获取after方法
Method after = class_getInstanceMethod(self, @selector(methodChangeAfter));
//交换两个方法的实现
method_exchangeImplementations(before, after);
}
- (void)methodChangeBefore
{
NSLog(@"methodChangeBefore");
}
- (void)methodChangeAfter
{
[self methodChangeAfter];
NSLog(@"methodChangeAfter");
}
@end
MethodChange *change = [MethodChange new];
[change methodChangeBefore];
2019-06-05 22:24:11.363371+0800 RuntimeTest[14603:26518469] methodChangeBefore
2019-06-05 22:24:11.363471+0800 RuntimeTest[14603:26518469] methodChangeAfter
动态添加方法
performSelector:
编译时没有这个方法,运行时确需要调用这个方法
void testImp (void)
{
NSLog(@"test invoke");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
// 如果是test方法 打印日志
if (sel == @selector(test)) {
NSLog(@"resolveInstanceMethod:");
// 动态添加test方法的实现
class_addMethod(self, @selector(test), testImp, "v@:");
return YES;
// return NO;
}
else{
// 返回父类的默认调用
return [super resolveInstanceMethod:sel];
}
}
动态方法解析
@dynamic
对属性标记为@dynamic
,不需要编译器自动为我们添加getter和setter的具体实现,而是在运行时我们具体的调用了getter和setter的时候,再去为他添加具体的实现。
相当于getter和setter是在运行时添加,而不是在编译时声明好具体的实现。
编译型语言和动态运行时语言的区别
- 动态运行时语言将函数决议推迟到运行时。
- 编译时语言在编译期进行函数决议。
[obj foo]
和objc_msgSend()
函数之间有什么关系?
[obj foo]
在编译期处理之后就变成了objc_msgSend(obj,@selector(foo))
函数,接下来就开始了objc_msgSend
函数传递过程。
runtime如何通过Selector找到对应的IMP
地址的?
首先查找当前实例所对应类对象的缓存是否有Selector对应缓存的IMP
的实现。 ,如果有就把缓存中的函数返回给调用方;
如果缓存中查找不到, 在根据当前类的方法列表去查找Selector对应的IMP
的实现;
如果当前类方法列表页没有查找到,在根据当前类的superClass指针,逐级查找父类的方法列表去去查找Selector对应的IMP
的实现。
能否向编译后的类中增加实例变量?
不能,class_ro_t
中ro代表readOnly,所以没有办法为期添加实例变量。
可以向动态添加的类中添加实例变量,只要在调用注册类对的方法之前,去完成实例变量的添加,就是可以实现的。