iOS底层原理之isa与类相关部分面试题分析

285 阅读3分钟

属性、成员变量与实例变量

属性

使用@property来声明的对象称为属性,将OC代码转成C++代码后,变量前面会代用_,比如_name,自动实现set与get方法

成员变量

成员变量是声明在{}

实例变量

实例变量是一种特殊的成员变量,它是类的实例化,在成员变量中基本上除了基本数据类型常量类型(比如NSString、NSArray等),剩余的都是实例变量

元类中存在类方法的探索

我们在 iOS底层原理之isa走位与类结构分析中探究类结构方法列表时已经知道实例方法存在类中,类方法存在元类中,那么为什么呢?我们先看代码

//在LGTeacher中
@interface LGTeacher : LGPerson
@property (nonatomic, copy) NSString *name;

-(void)sayHello;
+(void)sayGoodbye;
@end

@implementation LGTeacher
-(void)sayHello {
    
}
+(void)sayGoodbye {
    
}
@end

//在main文件中
void lgObjc_copyMethodList(Class pClass){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[i];
        //获取方法名
        NSString *key = NSStringFromSelector(method_getName(method));
        
        IFLog(@"Method, name: %@", key);
    }
    free(methods);
}

void lgInstanceMethod_classToMetaclass(Class pClass){
    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(sayGoodbye));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayGoodbye));
    NSLog(@"methord = %s",__func__);
    NSLog(@"\n%p\n%p\n%p\n%p",method1,method2,method3,method4);
}

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(sayGoodbye));
    Method method4 = class_getClassMethod(metaClass, @selector(sayGoodbye));
    NSLog(@"methord = %s",__func__);
    NSLog(@"\n%p\n%p\n%p\n%p",method1,method2,method3,method4);
}

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(sayGoodbye));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayGoodbye));
    NSLog(@"methord = %s",__func__);
    NSLog(@"\n%p\n%p\n%p\n%p",imp1,imp2,imp3,imp4);
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        LGTeacher *person = [LGTeacher alloc];
        Class pClass     = object_getClass(person);
        lgObjc_copyMethodList(pClass);
        
        lgInstanceMethod_classToMetaclass(pClass);
        lgClassMethod_classToMetaclass(pClass);
        lgIMP_classToMetaclass(pClass);
    }
    return 0;
}

代码运行结果如图所示

  • lgInstanceMethod_classToMetaclass用于获取类的实例方法
  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(sayGoodbye));
  Method method4 = class_getInstanceMethod(metaClass, @selector(sayGoodbye));

method1是在当前类(即LGTeacher)中查找 sayHellosayHello是实例方法,所以找到了。

method2是在LGTeacher的元类中查找sayHello,由于sayHello是实例方法,所以在元类中没有找到。

同理sayGoodbye是类方法,在LGTeacher类中肯定没有,它在LGTeacher的元类中,所以method3没有找到,method4找到了,打印结果就是

  • lgClassMethod_classToMetaclass 用于获取类的类方法这是重点、难点
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(sayGoodbye));
Method method4 = class_getClassMethod(metaClass, @selector(sayGoodbye));

method1是在当前类LGTeacher中查找,由于sayHello是实例方法,肯定找不到,同样method2是在LGTeacher的元类中也找不到。

method3方法是在当前类LGTeacher中查找,由于sayGoodbye是类方法,肯定能找到。 method4是在LGTeacher的元类中查找,我们从上面图中发现竟然也找到了,那么为什么元类中也存在sayGoodbye方法呢?重点来了,我们点击class_getClassMethod进去查看源码,这个重点是getMeta方法,我们进去继续查看,getMeta中我们得知:它先判断当前类是否是元类,如果是则返回。因此method4是在LGTeacher的元类中查找,因为类方法是元类的对象方法,所以肯定能找到

  • lgIMP_classToMetaclass 查找方法实现
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(sayGoodbye));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayGoodbye));

我们先看class_getMethodImplementation内部实现,在class_getMethodImplementation方法中,它会查找sel,如果找到则会返回,找不到就进行消息转发。因此, imp1方法是在LGTeacher类中查找sayHello的实现,找到了就返回;imp2在LGTeacher元类中查找sayHello,没有找到,进行了消息转发,在LGTeacher类中找到,所以返回。 同理imp3在LGTeacher类中没有找到sayGoodbye方法,进行消息转发,在LGTeacher元类(类方法存在元类中)找到了,imp4在LGTeacher的元类中找到,则返回。

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

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与isMemberofClass的内部实现

isKindofClass分析

isKindofClass的类方法实现

+ (BOOL)isKindOfClass:(Class)cls {
    // 类 vs 元类
    // 根元类 vs NSObject
    // NSObject vs NSObject
    // LGPerson vs 元类 (根元类) (NSObject)
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

isKindOfClass类方法中,判断的是当前类跟元类的对比,如果是则返回YES,否则返回NO。

isKindofClass的实例方法实现

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

在实例方法中判断的是当前类跟自己或者父类(直到父类为nil)对比,如果是则返回YES,否则返回NO。

  • objc_opt_isKindOfClass 本来我们以为在isKindOfClass方法中下断点肯定会走进来,然而这实际是个坑点,为什么这么说呢?因为当我们打断点时,无论isKindOfClass的类方法还是实例方法都没有进去,那么最终进入哪儿去了呢?我们在源码中搜索isKindOfClass,发现如下几个方法,我们在objc_opt_isKindOfClass中打断点,发现无论类方法还是实例方法都进来了,那么这个方法是干什么的呢?
/*
* 当传入是一个对象时,getIsa返回该对象的类;如果传入是一个类,则返回元类。
所以无论传入对象还是类,最终执行的效果跟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);
}

从上面objc_opt_isKindOfClass分析,我们可以得知:由于[NSObject class]是NSObject,它的元类也是NSObject,所以re1是YES; [LGPerson class]是LGPerson,它的元类是NSObject,所以re3为NO 由于[NSObject alloc]对象的类是NSObject,它是NSObject的类,所以re5返回YES;同理[LGPerson alloc]对象的类是LGPerson,跟[LGPerson class]相同,所以re7返回YES。

isMemberOfClass分析

isMemberOfClass类方法分析

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

isMemberOfClass类方法返回当前类跟其元类是否相同 [NSObject class]类是NSObject,NSObject的元类是根元类,所以不同,re2返回NO(根元类的元类指向是NSObject); [LGPerson alloc]类是LGPerson,其元类是NSObject,所以不同,re4返回NO。

isMemberOfClass实例方法分析

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

isMemberOfClass实例方法是当前类跟自身相比,相同返回YES,否则返回NO。 [NSObject alloc]对象的类是NSObject,跟NSObject相同,所以re6返回YES,同理re8也返回YES。