一、Runtime
runtime
称之为运行时
,与之相对的是编译时
运行时
,是代码跑起来,被装载到内存中
的过程,是动态
阶段,此时出错会导致程序崩溃
编译时
,是源代码翻译成机器能识别的代码
的过程,是静态
阶段,主要做一些词法分析
,语法分析
等操作
二、Runtime调用的三种途径
三、objc_msgSend
一、方法的本质
在isa结构分析中,我们通过Clang
查看了对象的本质
,同样的,这里我们一样可以使用Clang
查看方法的本质
//main函数中方法的调用
LGPerson *person = [LGPerson alloc];
[person sayHello];
//clang后的方法的调用
LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayHello"));
可以看出,方法的本质
是objc_msgSend(消息发送)
这里我们验证一下,它们是不是真的一样:
1、导入头文件
2、设置enable strict checking of obc_msgSend calls
为NO
(否则objc_msgSend的参数会报错)
LGPerson *person = [LGPerson alloc];
//1.方法的调用
[person sayHello];
//2.消息发送
objc_msgSend(person, sel_registerName("sayHello"));
打印结果如图:
可以看到,打印结果是一样的,所以
[person sayHello]
等价于objc_msgSend(person, sel_registerName("sayHello"))
二、方法的调用,会执行父类的实现
1、定义两个类LGTeacher
、LGPerson
@interface LGTeacher : NSObject
- (void)sayHello;
@end
@implementation LGTeacher
- (void)sayHello {
NSLog(@"666");
}
@end
@interface LGPerson : LGTeacher
- (void)sayHello;
- (void)sayNB;
@end
@implementation LGPerson
- (void)sayNB {
NSLog(@"666");
}
@end
可以看到,LGPerson
只声明了sayHello
方法却没有实现,但是父类LGTeacher
实现了
2、通过objc_msgSendSuper
调用父类方法
LGPerson *person = [LGPerson alloc];
LGTeacher *teacher = [LGTeacher alloc];
[person sayHello];
struct objc_super lgsuper;
lgsuper.receiver = person;//消息的接收者还是person
lgsuper.super_class = [LGTeacher class];//告诉父类是谁
////消息的接受者还是自己 - 父类 - 请你直接找我的父亲
objc_msgSendSuper(&lgsuper, sel_registerName("sayHello"));
objc_msgSendSuper
方法中有两个参数:
OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
#endif
结构体 objc_super
和sel
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 */
};
#endif
结构体
中需要指定receiver
和super_class
3、打印结果如图:
可以看出,无论是
[person sayHello]
还是objc_msgSendSuper(&lgsuper, sel_registerName("sayHello"))
执行的都是父类
中的实现
三、初探objc_msgSend
打开源码搜索objc_msgSend
,选择arm64.s
后缀的文件,可以查找objc_msgSend
的源码实现,发现是汇编实现
//---- 消息发送 -- 汇编入口--objc_msgSend主要是拿到接收者的isa信息
ENTRY _objc_msgSend
//---- 无窗口
UNWIND _objc_msgSend, NoFrame
//---- p0 和空对比,即判断接收者是否存在,其中p0是objc_msgSend的第一个参数-消息接收者receiver
cmp p0, #0 // nil check and tagged pointer check
//---- le小于 --支持taggedpointer(小对象类型)的流程
#if SUPPORT_TAGGED_POINTERS
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
//---- p0 等于 0 时,直接返回 空
b.eq LReturnZero
#endif
//---- p0即receiver 肯定存在的流程
//---- 根据对象拿出isa ,即从x0寄存器指向的地址 取出 isa,存入 p13寄存器
ldr p13, [x0] // p13 = isa
//---- 在64位架构下通过 p16 = isa(p13) & ISA_MASK,拿出shiftcls信息,得到class信息
GetClassFromIsa_p16 p13 // p16 = class
LGetIsaDone:
// calls imp or objc_msgSend_uncached
//---- 如果有isa,走到CacheLookup 即缓存查找流程,也就是所谓的sel-imp快速查找流程
CacheLookup NORMAL, _objc_msgSend
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
//---- 等于空,返回空
b.eq LReturnZero // nil check
// tagged
adrp x10, _objc_debug_taggedpointer_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
ubfx x11, x0, #60, #4
ldr x16, [x10, x11, LSL #3]
adrp x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
add x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
cmp x10, x16
b.ne LGetIsaDone
// ext tagged
adrp x10, _objc_debug_taggedpointer_ext_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_ext_classes@PAGEOFF
ubfx x11, x0, #52, #8
ldr x16, [x10, x11, LSL #3]
b LGetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif
LReturnZero:
// x0 is already zero
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
ret
END_ENTRY _objc_msgSend
整体执行的流程如图: