什么是Runtime
在对象上调用方法是Objective-C中经常使用的功能,用Objective-C的术语来说,这叫”传递消息”(pass a message),消息有”名称”(name)或”选择器”(selector),可以接受参数,而且还会有返回值。
将OC代码转换为C/C++代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
[person test];
}
return 0;
}
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
编译后的C/C++代码
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("test"));
void objc_msgSend(void / id self, SEL op, … / )
person: 消息接收者(receiver)
test: 消息名称
sel_registerName: 等价于@selector(test)
objc_msgSend函数会根据消息接收者与选择器的类型来调用方法,为了完成此操作,该方法调用如下:
![](https://img.haomeiwen.com/i9521271/83de8ce16cd2be87.png)
图片.png
从源代码来证明
首先在objc-msg-arm64.s文件找到_objc_msgSend程序实现,为了提高运行效率,苹果对这个方法做了很多优化,这个方法是用汇编实现的
// 入口
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
MESSENGER_START
// 如果_objc_msgSend第一个参数(消息接收者)不存在(小于等于0)
cmp x0, #0 // nil check and tagged pointer check
// 跳转到LNilOrTagged
b.le LNilOrTagged // (MSB tagged pointer looks negative)
ldr x13, [x0] // x13 = isa
and x16, x13, #ISA_MASK // x16 = class
LGetIsaDone:
// 如果�存在消息接受者就会查找缓存: 如果有缓存就返回IMP否则返回_class_lookupMethodAndLoadCache3函数
// 在runtime开源文件搜索该函数
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
LNilOrTagged:
b.eq LReturnZero // nil check
...
END_ENTRY _objc_msgSend
// 传入三个参数:消息接收者,类名,类对象
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// 因为cache传入的NO所以该方法不会调用,不会从缓存里面找
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
runtimeLock.read();
if (!cls->isRealized()) {
// Drop the read-lock and acquire the write-lock.
// realizeClass() checks isRealized() again to prevent
// a race while the lock is down.
runtimeLock.unlockRead();
runtimeLock.write();
realizeClass(cls);
runtimeLock.unlockWrite();
runtimeLock.read();
}
if (initialize && !cls->isInitialized()) {
runtimeLock.unlockRead();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.read();
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
retry:
runtimeLock.assertReading();
// 继续在缓存查找,因为上面代码可能会动态添加方法
imp = cache_getImp(cls, sel);
// 如果找到方法,直接返回IMP
if (imp) goto done;
// 如果缓存没有找到方法
{
// 传入类或者元类:内部找到class->class_rw_t->methods查找方法:如果方法是有序的就用二分查找,如果没有排序就一个个遍历查找
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
// 如果有方法就缓存到当前类对象缓存中
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
// 返回IMP
imp = meth->imp;
goto done;
}
}
// 如果当前类没有找到方法,会调用superclass中查找
{
unsigned attempts = unreasonableClassCount();
// 调用superclass循环查找方法
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// 从父类缓存查找
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// 缓存到消息接收者类缓存中
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// 如果父类缓存中没有方法:就从class->class_rw_t->methods查找方法:如果方法是有序的就用二分查找,如果没有排序就一个个遍历查找
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
// 缓存到消息接收者类缓存中
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
// 如果消息发送没有找到方法,系统会尝试动态方法解析
// 如果有消息接收者和没有动态解析过
if (resolver && !triedResolver) {
runtimeLock.unlockRead();
// 根据类对象或者元类调用[cls resolveInstanceMethod:sel]或[nonMetaClass resolveClassMethod:sel]
// 如果有实现以上方法,则会重新走消息发送否则进行消息转发
_class_resolveMethod(cls, sel, inst);
runtimeLock.read();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
// 如果动态方法解析也没有找到,就会调用消息转发
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlockRead();
return imp;
}
动态方法解析
![](https://img.haomeiwen.com/i9521271/90c39202e844d3ea.png)
图片.png
@interface Person : NSObject
- (void)test;
+ (void)resClassTest;
@end
@implementation Person
- (void)run {
NSLog(@"%s",__func__);
}
void instanceC_Run(id self, SEL _cmd) {
NSLog(@"%s",__func__);
}
+ (void)resClassRun {
NSLog(@"%s",__func__);
}
void resClassC_Run(id self, SEL _cmd) {
NSLog(@"%s",__func__);
}
// 动态方法解析
// 类方法
// 一
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(resClassTest)) {
class_addMethod(object_getClass(self), sel, (IMP)resClassC_Run, "v@:");
}
return [super resolveClassMethod:sel];
}
// 二
+ (BOOL)resolveClassMethod:(SEL)sel {
if (sel == @selector(resClassTest)) {
Method method = class_getClassMethod(object_getClass(self), @selector(resClassRun));
class_addMethod(object_getClass(self), sel, method_getImplementation(method), "v@:");
}
return [super resolveClassMethod:sel];
}
// 对象方法
// 一
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
class_addMethod(self, sel, (IMP)instanceC_Run, "v@:");
}
return [super resolveInstanceMethod:sel];
}
// 二
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(test)) {
Method method = class_getInstanceMethod(self, @selector(run));
class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
[person test];
[Person resClassTest];
}
return 0;
}
消息转发
![](https://img.haomeiwen.com/i9521271/233dcc0c01598fcd.png)
图片.png
@implementation Person
// 动态方法解析没有添加方法实现就会调用该方法
- (id)forwardingTargetForSelector:(SEL)aSelector
{ // 根据不同方法返回给Cat象调用test方法
if (aSelector == @selector(test)) return [[Cat alloc] init];
return [super forwardingTargetForSelector:aSelector];
}
// 返回方法签名
// 如果-forwardingTargetForSelector方法没有调用,就会调用该方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
// 返回值类型,参数类型
if (aSelector == @selector(test)) return [NSMethodSignature signatureWithObjCTypes:"v@:"];
return [super methodSignatureForSelector:aSelector];
}
// 调用methodSignatureForSelector后返回参数类型,参数类型后会调用该方法
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// 做一些拦截或者对象转发消息操作等
}
@end
网友评论