iOS 类02 - isa 经典面试题分析

328 阅读4分钟

[面试题]

1、属性&成员变量&实例变量的区别

属性 = 带下划线成员变量 + setter + getter 方法

实例变量:(成员变量中的对象变量 就是 实例变量):以实例对象实例化来的,是一种特殊成员变量

成员变量:写在类声明的大括号中的变量, 我们称之为成员变量。

NSString常量类型, 因为不能添加属性,如果定义在类中的{}中,是成员变量

注意: 成员变量不能离开类, 离开类之后就不是成员变量 成员变量不能在定义的同时进行初始化

存储: 堆(当前对象对应的堆的存储空间中)--存储在堆中的数据, 不会被自动释放, 只能程序员手动释放

成员变量中 除去基本数据类型、NSString,其他都是 实例变量(即可以添加属性的成员变量),实例变量主要是判断是不是对象

全局变量:写在函数和大括号外部的变量, 我们称之为全局变量。

作用域: 从定义的那一行开始, 一直到文件末尾
存储: 静态区
程序一启动就会分配存储空间, 直到程序结束才会释放

局部变量:

写在函数或者代码块中的变量, 我们称之为局部变量
作用域: 从定义的那一行开始, 一直到遇到大括号或者return
局部变量可以先定义再初始化, 也可以定义的同时初始化
存储 : 栈
存储在栈中的数据有一个特点, 系统会自动给我们释放

[2、类存在几份?]

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

[3、探索方法的归属问题?]

1、类的加载: 编译过程中是懒加载

2、 实例方法存储在中,类方法存储在元类中--所以方法永远是归属于

探索如下:

首先准备工作:创建一个 LGPerson

@interface LGPerson : NSObject
//类方法
- (void)sayHello;
//实例方法,对象方法
+ (void)sayHappy;
@end

调试调用:

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

获取类的类方法:lgClassMethod_classToMetaclass

 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));
    // 元类 为什么有 sayHappy 类方法 0 1
    //
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

执行结果如下:

class_getClassMethod 获取类方法

    //如果找到元类,就直接返回这个类,没有执行下去的必要,其目的是为了防止元类的无限递归查找
    Class getMeta() {
        if (isMetaClass()) return (Class)this;
        else return this->ISA();
    }

so:class_getClassMethod,获取类的方法,其实就是在获取元类的实例方法。

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

总结: 实例方法存储在中,类方法存储在元类中--所以方法永远是归属于class_getClassMethod,获取类的方法,其实就是在获取元类的实例方法。

[面试题4.oc自省函数,]

  • isKindOfClass & isMemberOfClass 示例:类调用2个方法 结果: 实例调用2个方法 结果: isMemberOfClass

    使用类方法结果解析

    1NSObjectNSObject 的对比,使用 +isKindOfClass
    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];   
    NSObject 根元类的 superclass 是其本身 NSObject
    so:re1 = yes;
    2BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; 
    NSObject (self->ISA());找到的是根元类 !=  NSObject
    so: re2 = NO
    3.BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]]; 
    LGPerson 类的self->ISA()找到元类 NSObjectNSObject-》tcls->superclass,= nil;
    so :re3 = NO4. BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];     //
    LGPerson类找到 self->ISA(),根元类NSObject != LGPerson;
    so re4 = NO,
    

    实例方法解析

     1、BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       
    	NSObject -》[self class],tcls->superclass
    	//传入的实例对象获取的类是NSObject,此实例对象的额父类还是NSObject
        so :re5=yes
     2、BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
    	[self class]->superclass;
        传入的实例对象,获取的类是NSObject,实例对象的父类:NSObject
        so:re6=yes
     3、BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];
        传入的实例对象[self class]-》 LGPerson,tcls->superclass -》LGPerson
        so:re7 = yes
     4、BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];
       传入的实例对象的[self class] ,LGPerson,= LGPerson
       so re8 = yes