iOS runtime整理

2017-10-18  本文已影响0人  孟子幻

runtime简介

Runtime 又叫运行时,是一套底层的 C 语言 API,是 iOS 系统的核心之一。开发者在编码过程中,可以给任意一个对象发送消息,在编译阶段只是确定了要向接收者发送这条消息,而接受者将要如何响应和处理这条消息,那就要看运行时来决定了。 C语言中,在编译期,函数的调用就会决定调用哪个函数。 而OC的函数,属于动态调用过程,在编译期并不能决定真正调用哪个函数,只有在真正运行时才会根据函数的名称找到对应的函数来调用。

typedef struct objc_class *Class;
typedef struct objc_object *id;

@interface Object { 
    Class isa; 
}

@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}

struct objc_object {
private:
    isa_t isa;
}

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
}

union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    Class cls;
    uintptr_t bits;
}

对象的实例方法调用时,通过对象的 isa 在类中获取方法的实现。
类对象的类方法调用时,通过类的 isa 在元类中获取方法的实现。


对象,类,元类之间的关系

SEL

typedef struct objc_selector *SEL;

objc_selector是一个映射到方法的C字符串。SEL是系统在编译过程中,会根据方法的名字以及参数序列生成一个用来区分这个方法的唯一ID编号,这个 ID 就是 SEL 类型的。我们需要注意的是,不同类中相同名字的方法所对应的方法选择器是相同的,即使方法名字相同而变量类型不同也会导致它们具有相同的方法选择器。由于这点特性,也导致了OC不支持函数重载。
获取SEL的几种方法:

SEL aSel = @selector(didReceiveMemoryWarning);
SEL a_sel = NSSelectorFromString(@"didReceiveMemoryWarning");
SEL a_Sel = sel_registerName("didReceiveMemoryWarning");
NSLog(@"%p___%p___%p",aSel,a_sel,a_Sel);

Method

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;  //方法名
    char *method_types                                       OBJC2_UNAVAILABLE;  //参数类型以及返回值类型编码
    IMP method_imp                                           OBJC2_UNAVAILABLE; //方法实现指针
}

获取方法

// 获取实例方法
Method class_getInstanceMethod ( Class cls, SEL name );
// 获取类方法
Method class_getClassMethod ( Class cls, SEL name );
// 获取所有方法的数组
Method * class_copyMethodList ( Class cls, unsigned int *outCount );

IMP

IMP即Implementation,是一个函数指针,指向的是函数的具体实现。在runtime中消息传递和转发的目的就是为了找到IMP,并执行函数。
获取IMP的方法:

//通过Method获取IMP
IMP method_getImplementation(Method m);
// 返回方法的具体实现
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );

runtime消息发送与转发

消息发送和转发
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    if (!cls->isRealized()) {
        realizeClass(cls);
    }
    if (initialize  &&  !cls->isInitialized()) {
        _class_initialize (_class_getNonMetaClass(cls, inst));
    }

 retry:    
    // Try this class's cache.
    imp = cache_getImp(cls, sel);
    if (imp) goto done;

    // Try this class's method lists.
    {
        Method meth = getMethodNoSuper_nolock(cls, sel);
        if (meth) {
            log_and_fill_cache(cls, meth->imp, sel, inst, cls);
            imp = meth->imp;
            goto done;
        }
    }

    // Try superclass caches and method lists.
    {
        unsigned attempts = unreasonableClassCount();
        for (Class curClass = cls->superclass;
             curClass != nil;
             curClass = curClass->superclass)
        {
            // Superclass cache.
            imp = cache_getImp(curClass, sel);
            if (imp) {
                if (imp != (IMP)_objc_msgForward_impcache) {
                    // Found the method in a superclass. Cache it in this class.
                    log_and_fill_cache(cls, imp, sel, inst, curClass);
                    goto done;
                }
                else {
                    // Stop searching, but don't cache yet; call method  resolver for this class first.
                    break;
                }
            }
            
            // Superclass method list.
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                imp = meth->imp;
                goto done;
            }
        }
    }

    // No implementation found. Try method resolver once.

    if (resolver  &&  !triedResolver) {
        _class_resolveMethod(cls, sel, inst);
        // 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;
    }

    // No implementation found, and method resolver didn't help.  Use forwarding.
    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);

    return imp;
}
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);
        }
    }
}
消息转发

动态方法解析
+resolveInstanceMethod:(实例方法)或者+resolveClassMethod:(类方法);我们有机会为该未知消息新增一个”处理方法””,不过使用该方法的前提是我们已经实现了该”处理方法”

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    NSString *selectorString = NSStringFromSelector(sel);
    if ([selectorString isEqualToString:@"methodO:"]) {
        Method addM = class_getInstanceMethod([self class], sel_registerName("functionMethodAddO:"));
        class_addMethod([self class], sel, method_getImplementation(addM), method_getTypeEncoding(addM));
    }
    return [super resolveInstanceMethod:sel];
}

备用接收者
如果一个对象实现了这个方法,并返回一个非nil的结果,则这个对象会作为消息的新接收者,且消息会被分发到这个对象

- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSString *selectorString = NSStringFromSelector(aSelector);
    Class cls = NSClassFromString(@"MrHelp");
    // 将消息转发给MrHelp类来处理
    if ([selectorString isEqualToString:@"methodTw"]) {
        return [cls new];
    }
    return [super forwardingTargetForSelector:aSelector];
}

完整消息转发
运行时系统会在这一步给消息接收者最后一次机会将消息转发给其它对象。对象会创建一个表示消息的NSInvocation对象,把与尚未处理的消息有关的全部细节都封装在anInvocation中,包括selector,目标(target)和参数。在这个方法中我们可以实现一些更复杂的功能,我们可以对消息的内容进行修改,比如追回一个参数等,然后再去触发消息

-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    Class cls = NSClassFromString(@"MrHelp");
    NSMethodSignature *singature= [super methodSignatureForSelector:aSelector];
    if (!singature) {
        singature = [cls instanceMethodSignatureForSelector:sel_registerName("universalMethod:AndClass:")];
    }
    return singature;
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
    NSString *selectorString = NSStringFromSelector(anInvocation.selector);
    Class cls = NSClassFromString(@"MrHelp");
    id methodStr =selectorString;
    id className = [anInvocation.target class];
    [anInvocation setArgument:&methodStr atIndex:2];//第一个参数
    [anInvocation setArgument:&className atIndex:3];//第而个参数
    
    [anInvocation setSelector:sel_registerName("universalMethod:AndClass:")];//universalMethod:AndClass:AndArg:
    [anInvocation invokeWithTarget:[cls new]];
}
上一篇下一篇

猜你喜欢

热点阅读