iOS-Developer-OCiOS - Developer - OC 进阶大全

Runtime消息机制

2019-08-01  本文已影响1人  zwwuchn

什么是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函数会根据消息接收者与选择器的类型来调用方法,为了完成此操作,该方法调用如下:

图片.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;
}

动态方法解析

图片.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;
}

消息转发

图片.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
上一篇下一篇

猜你喜欢

热点阅读