Runtime底层原理分析

97 阅读3分钟

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](