Runtime底层原理分析

2020-03-24  本文已影响0人  85ca4232089b

Runtime是一套API,有两个版本的:
一个是legacy Version版本。早期版本Objective-C 1.0 32位的Mac OSX的平台
一个是Modern Version版本。现行版本iPhone程序和Mac OS X V10.5以后的64系统

Objective-C程序有三种途径和运行时系统交互

  1. 通过 Objective-C源代码
  2. 通过Foundation框架中的 NSObject的方法
  3. 通过调用运行时系统提供给我们的API接口

对象方法和类方法

   Person *p = [[Person alloc] init];
   [p run];

clang 分析
终端命令 clang -rewrite-objc main.m -o main.cpp

typedef struct objc_object Person;
typedef struct {} _objc_exc_Person;

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
        ((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("run"));
// (void (*)(id, SEL))(void *)objc_msgSend)((id)p。  消息的接受者
// sel_registerName("run")   方法编号 
    }
    return 0;
}

Person编译成一个objc_object的结构体,OC对象的本质就是一个结构体
任何的方法调用的本质都是: 都会编译成objc_msgSend方法
• 对象方法发送消息

   objc_msgSend(p,sel_registerName("run"));
   NSLog(@"%p---%p",sel_registerName("run"),@selector(run));
// 0x7fff356b2cc2---0x7fff356b2cc2

• 类方法发送消息

   objc_msgSend(objc_getClass("LGStudent"),sel_registerName("walk"));

• 向父类发送消息(对象方法)

    struct objc_super cus;
    cus.receiver = p;
    cus.super_class = class_getSuperclass([p class]);
    objc_msgSendSuper(&cus, @selector(run));

• 向父类发送消息(类方法)

    struct objc_super cusSuper;
    cusSuper.receiver = [p class ];
    cusSuper.super_class = class_getSuperclass(objc_getClass([p class]));
    objc_msgSendSuper(&cusSuper, @selector(run));

objc_getClass([p class]):代表元类

  1. 对象方法是存在类上的
  2. 类方法是存在元类上的
  3. 对象和类分在在类和元类上都是以实例存在的,所以对象当发和实例方法都是以实例方法的形式存在的

消息查找&转发

汇编部分

• objc_msgSend
• LNilOrTagged tagged pointer 存储一些简单数据:NSNumber NSDadte 如果指针小于等于 LNilOrTagged 直接return返回
• LGetLsaDone isa 处理完毕
• CacheLookup 是个宏定义 NORMAL 缓存查找imp
  • call imp
  • objc_msgSend_uncached
  • CacheHit -> CALL
  • CheckMiss ->__objc_msgSend_uncached
   • 1 GETIMP-> LGetImpMiss
   • 2 NORMAL->__objc_msgSend_uncached->_class_lookupMethodAndLoadCache3 C函数
   • 3 LOOKUP->__objc_msgLookup_uncached
  • add
• MethodTableLookup
• bl __class_lookupMethodAndLoadCache3


runtime-汇编.png

消息查找&转发部分

动态方法解析

No implementation found. Try method resolver once.只会执行一次
• 对象方法 _class_resolveInstanceMethod
• 类方法 _class_resolveClassMethod

void _class_resolveMethod(Class cls, SEL sel, id inst)
{
    if (! cls->isMetaClass()) {//不是元类
        // try [cls resolveInstanceMethod:sel]
        _class_resolveInstanceMethod(cls, sel, inst);
    } 
    else {//是元类
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        _class_resolveClassMethod(cls, sel, inst);
        if (!lookUpImpOrNil(cls, sel, inst, 
                            NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
        {
            _class_resolveInstanceMethod(cls, sel, inst);
        }
    }
}

当我们实现这个方法的时候

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSLog(@"hellow");
return [super resolveInstanceMethod:sel];
}
动态方法解析.png

其实执行了两次,为什么呢


动态方法解析调用.png
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst){
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);
}

因为系统默认发送了一次消息:给当前类发送一个SEL_resolveInstanceMethod的消息.
另外说明一点:

 Method hellowordM1= class_getClassMethod(self, hellowordSEL);
Method hellowordM= class_getInstanceMethod(object_getClass(self), hellowordSEL);

• 类方法是存在元类上的
• 对象方法是存在类上面的
• 都是以实力对象的方式存在的
• 类的类方法和元类的对象方法是一样的

标准消息转发流程

_objc_msgForward_impcache
通过源码可以发现只有汇编的调用没有实现,但是我们可以通过instrumentObjcMessageSends 输出系统函数的调用

extern void instrumentObjcMessageSends(BOOL);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        instrumentObjcMessageSends(YES);
        [LGPerson  walk];
        instrumentObjcMessageSends(NO);
    }
    return 0;
}

运行之后可以发现在‎⁨Macintosh HD⁩ ▸ ⁨private⁩ ▸ ⁨tmp⁩路径下面会发现一个msgSends-54285的文件


消息转发的流程查看.png

从这里可以发现消息转发的流程:
• resolveInstanceMethod
• forwardingTargetForSelector
• methodSignatureForSelector
• forwardInvocation
• doesNotRecognizeSelector
实际的应用场景:
• 自定义事件处理。crash的手机 调用的堆栈信息,保存沙盒,上传服务器
• aop切面变成。aspect
• 数组越界的处理
• 消息转发..
• 运行时动态创建一些控制器等等,需要隐藏的页面

demo

上一篇下一篇

猜你喜欢

热点阅读