推广

iOS底层原理-几个面试题分析

iseeyu2年前 (2024-02-22)推广121

result.png

3.1)首先分析class_copyMethodList,从官方文档中可以得知,他是获取类实例方法,且不包含父类的实例方法。类没有实例方法或为nil,返回NULL。所以,只有earnMoney。

class_copyMethodList.png

3.2)cwInstanceMethod_classToMetaclass是通过class_getInstanceMethod获取实例方法,我们看看源码:

Method class_getInstanceMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    // This deliberately avoids +initialize because it historically did so.

    // This implementation is a bit weird because it's the only place that
    // wants a Method instead of an IMP.

#warning fixme build and search caches
        
    // Search method lists, try method resolver, etc.
    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();

    // fixme nil cls?
    // fixme nil sel?

    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());
    // fixme nil cls?
    // fixme nil sel?

    auto const methods = cls->data()->methods();
    for (auto mlists = methods.beginLists(),
              end = methods.endLists();
         mlists != end;
         ++mlists)
    {
        // <rdar://problem/46904873> getMethodNoSuper_nolock is the hottest
        // caller of search_method_list, inlining it turns
        // getMethodNoSuper_nolock into a frame-less function and eliminates
        // any store from this codepath.
        method_t *m = search_method_list_inline(*mlists, sel);
        if (m) return m;
    }

    return nil;
}

从源码中可以看到,根据传入的Class与SEL查找到Method。earnMoney是存储在类中的,元类中没有;spendMoney是存储在元类中的,元类中没有。

注意:
1.明明是去查找方法,为什么要调用lookUpImpOrForward?
lookUpImpOrForward是查找方法的实现,但是,在这个方法中,会检测CFI攻击及保证类进行了初始化。
2.CFI:控制流完整性(Control Flow Integrity, CFI)是由加州大学和微软公司于2005年提出的一种防御控制流劫持攻击的安全机制。

3.3)cwClassMethod_classToMetaclass是通过class_getClassMethod获取类方法的,我们可以看看源码

Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}
⬇️
Class getMeta() {
    if (isMetaClass()) return (Class)this;
    else return this->ISA();
}  

哎哟,类方法查找本质是查找元类的实例方法。在获取元类中,如果是元类,直接返回,不是元类会去获取元类。class_getClassMethod中的cls不管传入是类还是元类,只要sel是类方法且存在,就能找到改方法

3.4)cwIMP_classToMetaclass是通过class_getMethodImplementation函数获取imp的,获取结果:

image.png

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有两种,一种是方法的实现,一种是消息转发的函数指针。如果SEL没有实现,会返回消息转发函数指针,函数地址是一样的。所以,在类可以找到earnMoney方法实现,元类中可以找到spendMoney方法实现

4.iskindOfClass&isMemberOfClass的理解

iskindOfClass & isMemberOfClass 类方法调用

void isKindOrMember(){
    //-----使用 iskindOfClass & isMemberOfClass 类方法
    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];//1
    BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];//0
    BOOL re3 = [(id)[Person class] isKindOfClass:[Person class]];//0
    BOOL re4 = [(id)[Person class] isMemberOfClass:[Person class]];//0
    NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
    //iskindOfClass & isMemberOfClass 实例方法调用
    //------iskindOfClass & isMemberOfClass 实例方法
    BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];//1
    BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];//1
    BOOL re7 = [(id)[Person alloc] isKindOfClass:[Person class]];// 1
    BOOL re8 = [(id)[Person alloc] isMemberOfClass:[Person class]];//1
    NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
}

其最终结果打印如下:

class.png

我们看源码:

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

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

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

断点的时候,发现isKindOfClass、class不会被调用。why?一想肯定是llvm这个坏人干的好事儿,去一查,果然是他,发现他悄悄地将isKindOfClass换成了objc_opt_isKindOfClass,class换成objc_opt_class,所以,最总走的源码应该是:

// 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);
}

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

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

// Calls [obj class]
Class
objc_opt_class(id obj)
{
#if __OBJC2__
    if (slowpath(!obj)) return nil;
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        return cls->isMetaClass() ? obj : cls;
    }
#endif
    return ((Class(*)(id, SEL))objc_msgSend)(obj, @selector(class));
}

总结:
1.objc_opt_class很明显,如果传入的是实例对象,返回的是类对象。否则,传入什么返回什么(传入类对象返回该类对象,传入元类返回该元类)。
2.objc_opt_isKindOfClass方法是先获取传入对象的isa(类/元类),从isa开始查找,然后,从isa的继承链上查找。
3.+isMemberOfClass是取isa与传入对象比较
4.-isMemberOfClass是取类与传入对象比较

接下来,逐一解释:
4.1)BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];//1
objc_opt_class可以得知[NSObject class]返回的是类对象NSObject。由objc_opt_isKindOfClass可以的得知,将从元类NSObject开始遍历,并与类NSObject比较。由isa的走位可以知道元类NSObject的superclass是类NSObject,所以,NSObject元类.superclass == NSOject为true。

4.2)BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];//0
objc_opt_class可以得知[NSObject class]返回的是类对象NSObject。由isMemberOfClass类方法可以的得知,将从类NSObject取出isa,即元类NSObject与类NSObject比较。显然,这两个NSObject是不等的,一个是类,一个是元类。

4.3)BOOL re3 = [(id)[Person class] isKindOfClass:[Person class]];//0
objc_opt_class可以得知[Person class]返回的是类对象Person。由objc_opt_isKindOfClass可以的得知,将从元类Person开始遍历,并与类Person比较。由isa的走位可以知道:元类Personsuperclass是元类NSObject。所以,元类Person->元类NSObject->类NSObject->nil中没有一个和类Person相同。

4.4)BOOL re4 = [(id)[Person class] isMemberOfClass:[Person class]];//0
objc_opt_class可以得知[Person class]返回的是类对象Person。由isMemberOfClass可以的得知,将从类Person取出isa,即元类Person与类Person比较。显然,这两个Person是不等的,一个是类,一个是元类。

4.5)BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];//1
objc_opt_isKindOfClass可以的得知,将从实例NSObject中获取类NSObject,从类NSObject开始遍历,并与类NSObject比较。所以,类NSObject == 类NSObject为true。

4.6)BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];//1
isMemberOfClass实例方法可以的得知,将从实例NSObject取出class,由objc_opt_class可以得知[NSObject class]是类NSObject。即类NSObject与类NSObject比较。显然,这两个NSObject是等的。

4.7)BOOL re7 = [(id)[Person alloc] isKindOfClass:[Person class]];// 1
objc_opt_isKindOfClass可以的得知,将从实例Person中获取类Person,从类Person开始遍历,并与类Person比较。所以,所以,类Person == 类Person为true。

4.8)BOOL re8 = [(id)[Person alloc] isMemberOfClass:[Person class]];//1
isMemberOfClass实例方法可以的得知,将从实例Person取出class,由objc_opt_class可以得知[person class]是类Person,即类Person与类Person比较。显然,这两个Person是等的。

扫描二维码推送至手机访问。

版权声明:本文由西安泽虎代运营发布,如需转载请注明出处。

转载请注明出处https://www.0291.com.cn/post/55906.html

相关文章

产品营销策略分为哪几个部分?

产品营销策略分为哪几个部分?

说到,就绕不开P——从经典的4P到6P、7P甚至10P,虽然后来也有4C等新理论,但以4P为代表的理论仍然是营销的基础框架。所以产品营销策略,其实就是确定4P。一、什么是4P?20世纪的60年代,美国营销学者麦卡锡教授提出“产品、价格、、促销”4大营销组合策略,四个单词的第...

40个视频涨粉1400多万,令人惊叹的粉丝转化率。

40个视频涨粉1400多万,令人惊叹的粉丝转化率。

40个视频涨粉1400多万,这样令人惊叹的粉丝转化率,在一个名为“”的自媒体视频创作者身上实现了。人民网公开表扬“张同学”,赞扬其用专业的手段记录真实的乡村生活将乡村魅力展现在大众面前,这是具有正能量的视频内容,值得学习和发扬。并呼吁越来越多的“张同学”出现。    ...

营销策划方案案例(世界地球日品牌借势营销策略指南(附文案和创意话题活动案例))。

营销策划方案案例(世界地球日品牌借势营销策略指南(附文案和创意话题活动案例))。

#头条创作挑战赛# 2023年4月22日,将是第54个世界地球日(The Earth Day)。也只有这一天,我们能抛弃国际、种族等观念,统一认识到我们都是一家人——地球人。 这一非官方节日对消费者的吸引力、对世界的凝聚,天然适合品牌宣传其公益活动的性质,每年都是品牌展示环保理念的时间节点。...

抖音巨量千川该怎么投?

抖音巨量千川该怎么投?

  抖音巨量千川是电商广告平台,为抖音商家和创作者们提供电商营销解决方案,支持百应达人角色通过百应跳转千川进行直播推广,含直播、短视频多种带货形式。所以抖音巨量千川是一个其实就是一个整合了多端投放手段、融合了电商经营场景的一个超级平台,因此,玩转抖音,必须深谙抖音巨量千川。...

有效的渠道策划有哪些?

有效的渠道策划有哪些?

线下快销品的...

如何建设一个自己的博客网站。

如何建设一个自己的博客网站。

如果你想要通过互联网,就应该有创建一个自己的网站的这种冲动,要搭建一个属于自己的网络空间平台,但是苦于不知道从何下手,或者因为不知道水有多深,又或者觉得那是做IT,搞程序的人的专利,望而却步,本文绝对适合你。在开始之前我想说的是:搭建一个自己的网站没有你想的那么复杂,只要想搞(做),就可以。 前提...

现在,非常期待与您的又一次邂逅

我们努力让每一部企业宣传片和抖音短视频成为商业大片