推广

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

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

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的走位可以知道:元类Person的superclass是元类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

相关文章

西安企业做百度推广,一个月花三千和花三万到底差在哪?

4|最近跟几个西安的老板聊天,发现一个挺有意思的现象:同样做百度推广,有人一个月花三千块,询盘不断;有人一个月砸三万,水花都没见着。问他们区别在哪,大多数人说不清楚,只知道'钱花了,没效果'。 5| 6|百度推广这东西,说复杂也复杂,说简单也简单。核心就一件事:让你的潜在客户搜某个词的时候,你的信息...

我想当策划,大学应该选什么专业?

我想当策划,大学应该选什么专业?

学,广告,传播,公关,这些专业其实都可以。另外也可以学像汉语言文学,以及一些跟文学相关的一些专业,会对你找工作有帮助。但是,你想从事这个这份职业,除了专业的学习之外,更多的是,你要在上学的时候积极参与一些更营销策划相关的社会实践。比如说你可以做一些兼职,比如说帮别人去写稿,...

服装市场营销策略分析,“定位不慎满盘皆输”

服装市场营销策略分析,“定位不慎满盘皆输”

不同的区域需要不同的策略,而不同的营销策略需要不同的销售思路来配合。因此,在研究女装产品销售思路之前,最好先对于市场与营销策略做一次深入地了解。1.市场区域化(market segmentation):目标营销的第一步,是将整个市场拆分成若干个同性质的小市场。2.目标市场的...

晚间悦读丨你的思维方式,决定你的人生高度!

晚间悦读丨你的思维方式,决定你的人生高度!

越长大,越发现,能够决定你人生高度的,往往是你的认知层次。 你的认知在哪个层级,你的人生就处在什么状态。你认知的高度,决定了你人生的高度。 那如何才能升级方式、提升认知能力呢?可以从这三个方面做出改变。 NO. 1 改变态度 有人...

编写项目营销策划方案六大要点

编写项目营销策划方案六大要点

现在商业发展非常迅速,的需求非常的大,但是要做好营销策划是有一定难度的,我把自己做营销策划的一些体会在这里分享给朋友们。营销策划方案六大要点我们先说项目营销策划方案这个话题。大部分的营销策划人都是在企业里工作,大部分项目之初都是来源于一个想法,创始人、老板或者创始团队的一个...

【商场营销策划】如何进行商场营销策划?

【商场营销策划】如何进行商场营销策划?

商场是策划者们关注的永恒话题,那么究竟该如何作好商场营销策划呢?掌握以下这几点营销策略设计原则,至关重要!1.义利兼顾的原则。商场在获取利润的同时要充分考虑到消费者的合法利益和商场的长远利益。在作商场活动营销策略方案的时候应该将这一点作为首要的原则。对商场有利而对顾客不利的...

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

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