05 常見面試題

1. 下面的代碼輸出什么內(nèi)容?
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;

@end

@implementation LGPerson

- (void)sayHello{
    NSLog(@"LGPerson say : Hello!!!");
}

+ (void)sayHappy{
    NSLog(@"LGPerson say : Happy!!!");
}

@end

1.1

void lgInstanceMethod_classToMetaclass(Class pClass){
    // 其中pClass中sayHello定義為實(shí)例方法,sayHappy定義為類方法
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

解答:根據(jù)之前的學(xué)習(xí)我們知道其實(shí)并不存在什么類方法,類方法就是元類的實(shí)例方法,所以答案是:0xXXXXXXX-0x0-0x0-0xXXXXXXXX。

1.2

void lgClassMethod_classToMetaclass(Class pClass) {
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

解答:此題的難點(diǎn)在于class_getClassMethod的背后的實(shí)現(xiàn)原理。有下面的實(shí)現(xiàn)代碼可知class_getClassMethod其實(shí)就是找了傳入的cls的元類再調(diào)用class_getInstanceMethod函數(shù),這里面還有一個(gè)坑點(diǎn)在于getMeta函數(shù)的實(shí)現(xiàn),在調(diào)用getMeta函數(shù)時(shí)會(huì)先判斷當(dāng)前傳入的類是否是元類如果是直接返回當(dāng)前類,如果不是則通過isa返回元類。所以不論怎樣都會(huì)返回正確的元類。所以本題的答案是:0x0-0x0-0xXXXXXXXX-0xXXXXXXX

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

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

1.3

void lgIMP_classToMetaclass(Class pClass) {
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));

    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));

    NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
    NSLog(@"%s",__func__);
}

此題的難點(diǎn)還是在于函數(shù)的實(shí)現(xiàn),imp1和imp4毫無疑問肯定可以找到對應(yīng)的實(shí)現(xiàn),但是當(dāng)當(dāng)前類沒有對應(yīng)實(shí)現(xiàn)的時(shí)候會(huì)返回默認(rèn)的消息轉(zhuǎn)發(fā)實(shí)現(xiàn)_objc_msgForward

__attribute__((flatten))
IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

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

    lockdebug_assert_no_locks_locked_except({ &loadMethodLock });

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

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

    return imp;
}
2. iskindOfClass & isMemberOfClass 的理解
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

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

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

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
  • iskindOfClass & isMemberOfClass 類方法調(diào)用
//-----使用 iskindOfClass & isMemberOfClass 類方法
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     //
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];       //
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];     //
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);

解答:
isKindOfClass類方法實(shí)現(xiàn)會(huì)以當(dāng)前類的元類為初始值和傳入的類進(jìn)行比較,如果相同則返回YES,如果不相等則不停的取當(dāng)前類的元類的superclass與傳入的類進(jìn)行比較,如果相等則返回YES,如果一直到nil都沒有相等則返回NO。

isMemberOfClass類方法實(shí)現(xiàn)則是判斷當(dāng)前類的元類是否等于傳入類,如果相等返回YES,如果不相等返回NO。

當(dāng)傳入的類不是元類時(shí),進(jìn)行類方法的調(diào)用,isKindOfClass只有NSObject才會(huì)返回YES,isMemberOfClass則永遠(yuǎn)不會(huì)返回YES。故本題的答案是1-0-0-0。

  • iskindOfClass & isMemberOfClass 實(shí)例方法調(diào)用
//------iskindOfClass & isMemberOfClass 實(shí)例方法
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       //
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     //
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];       //
BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];     //
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);

解答:
isKindOfClass實(shí)例方法實(shí)現(xiàn)會(huì)以當(dāng)前實(shí)例的類為初始值和傳入的類進(jìn)行比較,如果相同則返回YES,如果不相等則不停的取當(dāng)前實(shí)例的類的superclass與傳入的類進(jìn)行比較,如果相等則返回YES,如果一直到nil都沒有相等則返回NO,簡單來說就是判斷當(dāng)前實(shí)例是否是傳入類及其所有子類的實(shí)例

isMemberOfClass類方法實(shí)現(xiàn)則是判斷當(dāng)前實(shí)例的類是否等于傳入類,如果相等返回YES,如果不相等返回NO。

故此題的答案是1-1-1-1。

總結(jié)
isKindOfClass
類方法:元類->父元類->...->根源類->根類傳入的類進(jìn)行比較,有一個(gè)相等則返回YES,沒有則返回NO。
實(shí)例方法:類->父類->...->根類傳入的類進(jìn)行比較,有一個(gè)相等則返回YES,沒有則返回NO。

isMemberOfClass
類方法:元類傳入的類進(jìn)行比較,相等則返回YES,不相等則返回NO。
實(shí)例方法:傳入的類進(jìn)行比較,相等則返回YES,不相等則返回NO。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容