类 和 isa 经典面试题分析

154 阅读2分钟

一 .方法存储

  • 类方法存储在元类里面,对象方法存储在类中;

类在内存中存在几份?

  • 由于类的信息在内存中永远只存在一份,所以 类对象只有一份

objc_object 与 对象的关系

  • 所有的对象 都是以 objc_object为模板继承过来的
  • 所有的对象 是 来自 NSObject(OC) ,但是真正到底层的 是一个objc_object(C/C++)的结构体类型
  • 【总结】 objc_object 与 对象的关系 是 继承关系

什么是 属性 & 成员变量 & 实例变量 ?

  • 属性:setter+getter+带下划线的成员变量
  • 实例变量:特殊的成员变量没有setter和getter方法(类的实例化)
  • 实例变量:通过当前对象类型,具备实例化的变量,是一种特殊的成员变量,例如 NSObject、UILabel、UIButton等

成员变量 和 实例变量什么区别?

  • 实例变量(即成员变量中的对象变量 就是 实例变量):以实例对象实例化来的,是一种特殊的成员变量
  • NSString 是常量类型, 因为不能添加属性,如果定义在类中的{}中,是成员变量
  • 成员变量中 除去基本数据类型、NSString,其他都是 实例变量(即可以添加属性的成员变量),实例变量主要是判断是不是对象

元类 中为什么会有 类对象 的 类方法?

在中的探索中,我们知道了实例方法 存储在类中,类方法存储在元类中

为了探索我们的面试题现象,定义了以下几个方法,来探索方法的归属问题

  • 在LGPerson中定义一个实例方法和一个类方法
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;

@end

@implementation LGPerson

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

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

@end

  • 实现方法 main方法调用
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        // 0x0000000100000000
        // LGTeacher *teacher = [LGTeacher alloc];

        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        lgObjc_copyMethodList(pClass);

        //lgInstanceMethod_classToMetaclass(pClass);
        //lgClassMethod_classToMetaclass(pClass);
        lgIMP_classToMetaclass(pClass);
        
        NSLog(@"Hello, World!");
    }
    return 0;
}

  • lgObjc_copyMethodList 获取类中所有方法:
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));
        
        LGLog(@"Method, name: %@", key);
    }
    free(methods);
}

  • lgInstanceMethod_classToMetaclass获取实例方法
void lgInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    //- (void)sayHello;
    //+ (void)sayHappy;
    //The method that corresponds to the implementation of the selector specified by aSelector for the class specified by aClass, or NULL if the specified class or its superclasses do not contain an instance method with the specified selector.
    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);
    //1 0 0 1
    // 0x1000081b0 - 0x0 - 0x0 - 0x100008148
}
  • lgClassMethod_classToMetaclass函数获取类方法
void lgClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    //- (void)sayHello;
    //+ (void)sayHappy;
    //A pointer to the Method data structure that corresponds to the implementation of the selector specified by aSelector for the class specified by aClass, or NULL if the specified class or its superclasses do not contain a class method with the specified selector.
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    // 元类 为什么有 sayHappy 类方法 0 1
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
    
    // 0 0 1 1
    //-0x0 -0x0 -0x100008148 -0x100008148
}

  • lgIMP_classToMetaclass函数获取方法实现
void lgIMP_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    // - (void)sayHello;
    // + (void)sayHappy;
    //The function pointer that would be called if [object name] were called with an instance of the class, or NULL if cls is Nil.
    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__);
    // 1 1 1 1 都有地址,
}
  • 获取类方法源码

然后再来看该方法的源码实现,可以得出class_getClassMethod的实现是获取类的类方法,其本质就是获取元类的实例方法,最终还是会走到class_getInstanceMethod,但是在这里需要注意的一点是:在getMeta源码中,如果判断出cls是元类,那么就不会再继续往下递归查找,会直接返回this,其目的是为了防止元类的无限递归查找

//获取类方法
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

⬇️
//获取元类
 // NOT identical to this->ISA when this is a metaclass 判断是否是元类,是元类就直接返回,反之,继续找isa指向
Class getMeta() {
    if (isMetaClass()) return (Class)this;
    else return this->ISA();
}

  • 小结:对于这些方法可以通过command+shift+0查看官方文档解释,也可以通过官方开源代码查看方法的调用情况。