推广

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

iseeyu3年前 (2024-02-22)推广126

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

相关文章

什么是商业思维,如何提升商业思维?

什么是商业思维,如何提升商业思维?

商业简单来说,就是你在看到一件商业事件后,能理解这背后的逻辑和思维模式,从而做出你的判断。举个落地的例子,比如我玩股票, 我买腾讯股票是因为我看好腾讯未来能够复合增长;我买天齐锂业是因为我对于电动车上下游行业的看好;我买欧派家居也是因为我偶然看到家居行业因为房地产的暴卖,判...

25个年度汽车营销案例,揭示10大趋势

25个年度汽车营销案例,揭示10大趋势

当前,产业正经历着百年未有之大变局,电动化、智能化转型正在改写着汽车产业竞争格局,汽车的产品属性、产业价值链和生态结构等正在被科技力量和想象力重构,汽车的也在不断的迭代和升级,而普通意义的常规动作,已很难触动消费者。围绕品牌主线,寻找更多跨界破圈的方法,这是大多数汽车品牌都...

常见被拒广告投放素材及应对技巧!

常见被拒广告投放素材及应对技巧!

  在日常工作中,小编经常听到运营/推广小伙伴们的哀嚎:“啊!!!素材又双叒叕被拒了!” 面对运营/推广小伙伴们“素材经常被拒”的痛点,今天美数君就通过列举一些广告素材被拒的常见典型案例,来为您阐述一下素材被拒的原因及素材制作原则,以便您轻松过审,顺利投放~ 一、游戏行业...

信息流推广是干什么的

首先什么是信息流,很多人听到这个词的时候心里其实并没有什么具体的概念,但是仔细分析下来就很容易明白了。信息流简单的解释就是指“信息流量”“信息流动”它不仅仅是指信息的集合同时更主要的一点就是指的是信息的流动传递,你能够将信息指定地传递,流通到各个地方。这就是信息流。是在社交...

小红书推广方法及上热门技巧!

小红书推广方法及上热门技巧!

  小红书是网络营销中拥有着庞大女性群体流量的核心平台。现在,小红书给大众的印象,更像是一个明星、网红分享“心得”,为品牌带货、为网友的种草平台。那么我们应该如何玩转小红书呢?     一、小红书定律 小红书上的受众人...

不同时期的网站seo优化方法。

不同时期的网站seo优化方法。

单一关键词或网站中心词的区分。100以外。中心关键词在100名以外,这个时分无外乎两种状况:一种状况便是这个网站是个新站,新站没有排名归于正常状况,不过有的时分也会呈现网站刚上线一个月,立马就有了排名,可是过了一两个星期之后没有了排名,这也是归于正常状况的。 若新站上线两三个月了,依...

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

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