底层

iOS-OC底层05:面试题分析

2020-09-15  本文已影响0人  MonKey_Money

实例方法和类方法的分析

@interface LGPerson : NSObject

@property (nonatomic, copy) NSString *name;
-(void)myInstanceMethod;

+(void)myClassMethod;
@end
@implementation LGPerson
+(void)myClassMethod {
    
}
-(void)myInstanceMethod {
    
}
@end
@interface LGTeacher : LGPerson
-(void)myTeacherInstanceMethod;

+(void)myTeacherClassMethod;
@end
@implementation LGTeacher

-(void)myTeacherInstanceMethod {
    
}

+(void)myTeacherClassMethod {
    
}
@end

我们通过class的c++实现objc_class,在进行内存平移访问,得出LGPerson的方法共4个方法分别如下

(lldb) p $6.get(0)
(method_t) $7 = {
  name = "myInstanceMethod"
  types = 0x0000000100000f82 "v16@0:8"
  imp = 0x0000000100000dc0 (KCObjc`-[LGPerson myInstanceMethod])
}
(lldb) p $6.get(1)
(method_t) $8 = {
  name = ".cxx_destruct"
  types = 0x0000000100000f82 "v16@0:8"
  imp = 0x0000000100000e30 (KCObjc`-[LGPerson .cxx_destruct])
}
(lldb) p $6.get(2)
(method_t) $9 = {
  name = "name"
  types = 0x0000000100000f96 "@16@0:8"
  imp = 0x0000000100000dd0 (KCObjc`-[LGPerson name])
}
(lldb) p $6.get(3)
(method_t) $10 = {
  name = "setName:"
  types = 0x0000000100000f9e "v24@0:8@16"
  imp = 0x0000000100000e00 (KCObjc`-[LGPerson setName:])
}

我们没看到类方法我们猜测类方法可能在元类中,

(lldb) p  $16.list
(method_list_t *const) $17 = 0x0000000100002088
(lldb) p *$17
(method_list_t) $18 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "myClassMethod"
      types = 0x0000000100000f82 "v16@0:8"
      imp = 0x0000000100000db0 (KCObjc`+[LGPerson myClassMethod])
    }
  }
}
(lldb) p $18.get(0)
(method_t) $19 = {
  name = "myClassMethod"
  types = 0x0000000100000f82 "v16@0:8"
  imp = 0x0000000100000db0 (KCObjc`+[LGPerson myClassMethod])
}

具体寻址过程,可以参考类结构分析

class_getInstanceMethod

从源码看class_getInstanceMethod的方法执行过程
class_getInstanceMethod===>_class_getMethod()===>getMethod_nolock()


instanceMethod.png
        LogInstanceMethod_classAndMetaClass([LGTeacher class]);

void LogInstanceMethod_classAndMetaClass(Class pClass) {
    const char * className =class_getName(pClass);
   Class metaClass  = objc_getMetaClass(className);
    Method method1 = class_getInstanceMethod(pClass, @selector(myTeacherInstanceMethod));
    Method method2 = class_getInstanceMethod(pClass, @selector(myTeacherClassMethod));
    Method method3 = class_getInstanceMethod(pClass, @selector(myInstanceMethod));
    Method method4 = class_getInstanceMethod(pClass, @selector(myClassMethod));
    
    Method method5 = class_getInstanceMethod(metaClass, @selector(myTeacherInstanceMethod));
    Method method6 = class_getInstanceMethod(metaClass, @selector(myTeacherClassMethod));
    Method method7 = class_getInstanceMethod(metaClass, @selector(myInstanceMethod));
    Method method8 = class_getInstanceMethod(metaClass, @selector(myClassMethod));
    NSLog(@"\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p",method1,method2,method3,method4,method5,method6,method7,method8);
    
}
打印结果
0x100002110
0x0
0x1000021e0
0x0
0x0
0x1000020a8
0x0
0x100002178

class_getInstanceMethod方法总结

LGTeacher.class
@selector(myTeacherInstanceMethod):自己的实例方法,所以有值
@selector(myTeacherClassMethod):自己的类方法,返回为空
@selector(myInstanceMethod):父类的实例方法,因为class_getInstanceMethod会遍历LGTeacher的父类,所以有返回值
@selector(myClassMethod):父类的类方法,所以返回有值
LGTeacher的元类分析,LGTeacher的元类的实例方法也就是LGTeacher类的类方法
@selector(myTeacherInstanceMethod):元类的实例方法是本类的类方法,所以返回为空
@selector(myTeacherClassMethod):本类的类方法就是本元类的实例方法,所以返回有值
@selector(myInstanceMethod):本类的实例方法,不在本元类的实例方法中,所以为空
@selector(myClassMethod):通过流程图可知,本类的实例方法还包括父类的实例方法,所以父类的类方法也是本元类的实例方法(会通过继承树寻找)

class_getClassMethod

我们先看一下源码

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

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

class_getClassMethod实质是让元类调用实例方法。先拿到传来类的元类,如果传来的类本来是元类,直接return
我们根据class_getInstanceMethod可知,class_getClassMethod是取类方法和父类的类方法,如果cls是元类,也会取到类方法和本类的父类类方法
验证

void logClassMethod_classAndMetaClass(Class pClass) {
     const char * className =class_getName(pClass);
    Class metaClass  = objc_getMetaClass(className);
     Method method1 = class_getClassMethod(pClass, @selector(myTeacherInstanceMethod));
     Method method2 = class_getClassMethod(pClass, @selector(myTeacherClassMethod));
     Method method3 = class_getClassMethod(pClass, @selector(myInstanceMethod));
     Method method4 = class_getClassMethod(pClass, @selector(myClassMethod));
     
     Method method5 = class_getClassMethod(metaClass, @selector(myTeacherInstanceMethod));
     Method method6 = class_getClassMethod(metaClass, @selector(myTeacherClassMethod));
     Method method7 = class_getClassMethod(metaClass, @selector(myInstanceMethod));
     Method method8 = class_getClassMethod(metaClass, @selector(myClassMethod));
     NSLog(@"\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p",method1,method2,method3,method4,method5,method6,method7,method8);
}
打印日志
0x0 //类的实例方法为空
0x1000020b0 //返回类的类方法
0x0 //父类的实例方法为空
0x100002180//返回父类的类方法
0x0 //类的实例方法为空
0x1000020b0//返回类的类方法
0x0 //父类的实例方法为空
0x100002180//返回父类的类方法

*** 类和元类调用class_getClassMethod,效果相同,因为class_getClassMethod的底层是调用元类的class_getInstanceMethod,在从类得到元类中有一个判断如果传入的类为元类直接返回,如果类不是元类,则通过isa取到元类***

isKindOfClass和isMemberOfClass面试题

isKindOfClass源码分析,通过探索底层的方法,我们在源码中定位到objc_opt_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);
}

函数分析,通过isa寻找类或者元类,如果obj是通过类初始化的对象,则cls是本类,如果obj本来是类,则cls是本类的元类。遍历父类和otherClass对比。
下面一个实例说明

    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //
    BOOL re2 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       //
       BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
    BOOL re4 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];       //
    BOOL re5 = [(id)[LGTeacher alloc] isKindOfClass:[LGPerson class]];
       NSLog(@" \nre1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\nre5 :%hhd\n",re1,re2,re3,re4,re5);

打印结果
re1 :1
 re2 :1
 re3 :0
 re4 :1
re5 :1
isa流程图.png

1 . [(id)[NSObject class] isKindOfClass:[NSObject class]]; //
通过上面源码可知,(id)[NSObject class]在内部实现中被转化成NSObject元类并沿着NSObject元类继承树和NSObject对比,我们知道NSObject的元类的父类是NSObject所以返回为true
2 . [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; //
[NSObject alloc]被转化成NSObject类,NSObject类和[NSObject class]相同所以返回为true

  1. [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
    LGPerson的元类和LGPerson元类的父类是否等于LGPerson,我们从上图中可知不想等
  2. [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; //
    [LGPerson alloc]被转化成LGPerson类和LGPerson类比较相同,所以返回是true
  3. [(id)[LGTeacher alloc] isKindOfClass:[LGPerson class]];
    [LGTeacher alloc]被转化成LGTeacher类,LGTeacher类和LGTeacher类的父类同LGPerson对比,可知LGTeacher的父类就是LGPerson,所以返回是true/

isMemberOfClass源码分析

isMemberOfClass底层实现有两个,一个类方法一个实例方法

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

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

类方法是元类和cls做对比

实例方法是通过[self class]取到对象所属的类,然后做对比。
下面示例

   BOOL re6 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
       BOOL re7 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
      BOOL re8 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];     //
       BOOL re9 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];
    BOOL re10 = [(id)[LGTeacher alloc] isMemberOfClass:[LGPerson class]];
 Class metaPerson = objc_getMetaClass("LGPerson");
    BOOL re11 = [(id)[LGPerson class] isMemberOfClass:metaPerson];     //
       NSLog(@" \nre6 :%hhd\n re7 :%hhd\n re8 :%hhd\n re9 :%hhd\nre10 :%hhd\n",re6,re7,re8,re9,re10);
打印结果
re6 :0
 re7 :1
 re8 :0
 re9 :1
re10 :0
re11 :1
  1. [(id)[NSObject class] isMemberOfClass:[NSObject class]]
    会调用底层实现的类方法,取NSObject的元类和NSObject做对比,所以返回是false
  2. [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]
    调用底层的实例方法,拿到[NSObject alloc] 对象的类NSObject,然后和NSObject对比返回是true
  3. [(id)[LGPerson class] isMemberOfClass:[LGPerson class]]
    调用底层的类方法,LGPerson的元类和LGPerson对比返回是false
  4. [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]
    调用底层的实例方法,LGPerson类和LGPerson类做对比返回是true
  5. [(id)[LGTeacher alloc] isMemberOfClass:[LGPerson class]]
    调用底层的实例方法,LGTeacher类和LGPerson对对比,返回是false。
  6. [(id)[LGPerson class] isMemberOfClass:metaPerson]
    调用底层的类方法,LGPerson的元类和LGPerson的元类对比返回时true。

总结

isKindOfClass和isMemberOfClass

两个都分为类调用或者对象调用,类调用是类的元类做判断,对象调用的是类做判断,另外isKindOfClass会沿着继承树和后面的类做对比,而isMemberOfClass只有本类(或者元类)做对比

class_getInstanceMethod和class_getClassMethod

元类的实例方法就是类的类方法
class_getInstanceMethod如果是类调用,类及父类的实例方法方法返回为YES,如果是元类调用的,类的类方法及父类的类方法返回YES。
class_getClassMethod如果是类调用,类及父类的类方法返回为YES,如果是元类调用,类的类方法和父类的类方返回是YES.因为class_getClassMethod中调用的是class_getInstanceMethod,传入的类是cls->getMeta(),getMeta会取cls的元类,如果cls本来就是元类会直接返回cls。

上一篇 下一篇

猜你喜欢

热点阅读