iOS底层

类(二)-- method归属

2020-09-15  本文已影响0人  过气的程序员DZ

类(一)-- 底层探索
类(二)-- method归属
类(三)-- cache_t分析

本文内容:

  1. OC方法中的sel和imp
  2. 类中method的归属以及一些api的实现
  3. isKindOfClass & isMemberOfClass

1、OC方法中的sel和imp


OC是对C/C++的超集(此处不是错别字,超级集合的含义),也可以说OC是一种上层封装。OC中方法调用,为了能够找到指定的方法实现,就得对方法做一个“标识”,然后通过这个“标识”去找到具体的实现位置。方法中的selimp就是为了实现这个“功能”。

可以把sel看做一本书目录中的一条信息,imp看做信息的页码。通过这条信息,找到指定页码,就可以翻到这页来查看相关内容。

2、类中method的归属


类中的实例方法(-方法)存在当前类中,类方法(+方法)存在元类中。其实是类的isa走位有关系,通过runtime的一些api可以进行确认。

定义一个DZPerson类,类中分别定义了一个实例方法(sayHi)和一个类方法(say666):

@interface DZPerson : NSObject
- (void)sayHi;
+ (void)say666;
@end

@implementation DZPerson

- (void)sayHi{
    NSLog(@"%s", __func__);
}

+ (void)say666{
    NSLog(@"%s", __func__);
}
@end

⏬⏬⏬

//调用
DZPerson *per = [DZPerson alloc];
Class pCla = object_getClass(per);
Class metaCla = objc_getMetaClass("DZPerson");

2.1 class_getInstanceMethod

class_getInstanceMethod作用:获取类的实例方法

2.1.1 示例:
Method m1 = class_getInstanceMethod(pCla, @selector(sayHi));
Method m2 = class_getInstanceMethod(metaCla, @selector(sayHi));
    
Method m3 = class_getInstanceMethod(pCla, @selector(say666));
Method m4 = class_getInstanceMethod(metaCla, @selector(say666));

NSLog(@"m1:%p", m1);
NSLog(@"m2:%p", m2);
NSLog(@"m3:%p", m3);
NSLog(@"m4:%p", m4);

输出结果:


接着看看源码中class_getInstanceMethod如何实现

2.1.2 class_getInstanceMethod源码分析
Method class_getInstanceMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;
#warning fixme build and search caches
    lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);

#warning fixme build and search caches
    return _class_getMethod(cls, sel);
}

⏬⏬⏬

static Method _class_getMethod(Class cls, SEL sel)
{
    mutex_locker_t lock(runtimeLock);
    return getMethod_nolock(cls, sel);
}

⏬⏬⏬

static method_t *
getMethod_nolock(Class cls, SEL sel)
{
    method_t *m = nil;
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    while (cls  &&  ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {
        cls = cls->superclass;
    }

    return m;
}

⏬⏬⏬

static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
    runtimeLock.assertLocked();

    ASSERT(cls->isRealized());

    auto const methods = cls->data()->methods();
    for (auto mlists = methods.beginLists(),
              end = methods.endLists();
         mlists != end;
         ++mlists)
    {
        method_t *m = search_method_list_inline(*mlists, sel);
        if (m) return m;
    }

    return nil;
}

通过源码推导出的简要流程图:


其中最核心的代码就是getMethodNoSuper_nolock函数中的auto const methods = cls->data()->methods();这行代码(图中红色部分)。从当前类中获取方法列表methods(),找到了直接返回;找不到再去父类中查找。

通过源码分析,很容易清楚class_getInstanceMethod示例的运行结果。

2.2 class_getClassMethod

class_getClassMethod作用:获取类方法

2.2.1 示例
Method m5 = class_getClassMethod(pCla, @selector(sayHi));
Method m6 = class_getClassMethod(metaCla, @selector(sayHi));
    
Method m7 = class_getClassMethod(pCla, @selector(say666));
Method m8 = class_getClassMethod(metaCla, @selector(say666));
NSLog(@"m5:%p", m5);
NSLog(@"m6:%p", m6);
NSLog(@"m7:%p", m7);
NSLog(@"m8:%p", m8);

输出结果:


2.2.2 class_getClassMethod源码分析
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;
    
    //注意cls->getMeta()
    return class_getInstanceMethod(cls->getMeta(), sel);
}

⏬⏬⏬

Class getMeta() {
    if (isMetaClass()) return (Class)this;//元类直接返回
    else return this->ISA();
}

通过源码的查看,我们了解到示例中m7m8都有值的原因。在调用class_getClassMethod时,分别传入的第一个参数是类和元类。

2.3 class_getMethodImplementation

class_getMethodImplementation作用:查找方法的imp

2.3.1 示例
IMP imp1 = class_getMethodImplementation(pCla, @selector(sayHi));
IMP imp2 = class_getMethodImplementation(metaCla, @selector(sayHi));

IMP imp3 = class_getMethodImplementation(pCla, @selector(say666));
IMP imp4 = class_getMethodImplementation(metaCla, @selector(say666));

NSLog(@"imp1:%p", imp1);
NSLog(@"imp2:%p", imp2);
NSLog(@"imp3:%p", imp3);
NSLog(@"imp4:%p", imp4);

输出结果:


image.png
2.3.2 class_getMethodImplementation源码分析
IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

    if (!cls  ||  !sel) return nil;

    imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);

    // Translate forwarding function to C-callable external version
    if (!imp) {
        return _objc_msgForward;
    }

    return imp;
}

通过源码看到,如果找不到imp,就会同意调用_objc_msgForward这个函数,这就是imp能找到并且返回相同地址原因。

3、isKindOfClass & isMemberOfClass


这两个方法在正常开发流程中,使用率还是挺高的。但是很多人可能不了解,这两个方法也有类方法(+方法)。先来看看示例:

3.1 示例

BOOL re1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [[DZPerson class] isKindOfClass:[DZPerson class]];
BOOL re4 = [[DZPerson class] isMemberOfClass:[DZPerson class]];
NSLog(@"re1 :%d", re1);
NSLog(@"re2 :%d", re2);
NSLog(@"re3 :%d", re3);
NSLog(@"re4 :%d", re4);

NSLog(@"====================分割线====================");

BOOL re5 = [[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [[DZPerson alloc] isKindOfClass:[DZPerson class]];
BOOL re8 = [[DZPerson alloc] isMemberOfClass:[DZPerson class]];
NSLog(@"re5 :%d", re5);
NSLog(@"re6 :%d", re6);
NSLog(@"re7 :%d", re7);
NSLog(@"re8 :%d", re8);

注意:上半部分调用的是类方法,下半部分调用实例方法。

输出结果:

re1 :1
re2 :0
re3 :0
re4 :0
====================分割线====================
re5 :1
re6 :1
re7 :1
re8 :1

3.2 isKindOfClass & isMemberOfClass源码

先把isa走位图放在这里,请再仔细看看


3.2.1 objc_opt_isKindOfClass源码

接着我们看源码:
此处需要注意,isKindOfClass的imp是objc_opt_isKindOfClass使用汇编调试的时候可以确认这点

此处和alloc方法一样,在llvm编译的时候,苹果底层进行了hook

接下来看看objc_opt_isKindOfClass实现,解释一下上面的示例

// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
    if (slowpath(!obj)) return NO;
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        for (Class tcls = cls; tcls; tcls = tcls->superclass) {
            if (tcls == otherClass) return YES;
        }
        return NO;
    }
#endif
    return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}

了解完实现再来对示例中代码解读一下:

3.2.2 isMemberOfClass源码
- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

3.3 小结

上一篇 下一篇

猜你喜欢

热点阅读