iOS-10.类的归属

539 阅读3分钟

ios底层文章汇总

1. 类定义

1.1 LGPerson类定义

@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end

1.2 关于类的归属的测试题


#ifdef DEBUG
#define LGLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]);
#else
#define LGLog(format, ...);
#endif

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);
        //Method, name: sayHello
    }
    free(methods);
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    Method *metalMethods = class_copyMethodList(metaClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const metalMethod = metalMethods[i];
        //获取方法名
        NSString *key = NSStringFromSelector(method_getName(metalMethod));
        
        LGLog(@"metal Method, name: %@", key);
        //metal Method, name: sayHappy
    }
    free(metalMethods);
    
}

void lgInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    //- (void)sayHello;
    //+ (void)sayHappy;
    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));
    //lgInstanceMethod_classToMetaclass - 0x1000031b0-0x0-0x0-0x100003148
    LGLog(@"%s - %p-%p-%p-%p",__func__,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(sayHappy));
    //- (void)sayHello;
    //+ (void)sayHappy;
    // 元类 为什么有 sayHappy 类方法 0 1
    /*
    class_getClassMethod的底层是获取元类的class_getInstanceMethod方法。
    class_getInstanceMethod(cls->getMeta(),sel)
    如果当前的类cls是元类,则cls->getMeta()接返回cls
    在当前类(元类)cls中可以找到找到实例方法sayHappy
    */
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    //lgClassMethod_classToMetaclass-0x0-0x0-0x100003148-0x100003148
    LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

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

    // - (void)sayHello;
    // + (void)sayHappy;
    /*
    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; //没有找到方法实现,返回_objc_msgForward函数指针
    }

    return imp;
}
    */
    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello)); //对象方法存储在类中
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello)); //没有找到方法实现,返回_objc_msgForward函数指针

    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));  //没有找到方法实现,返回_objc_msgForward函数指针 可以通过断点后打印 p imp3 查看 **(IMP) $0 = 0x7fff71092580 (libobjc.A.dylib`_objc_msgForward)**
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy)); //类方法存储在元类中
    //lgIMP_classToMetaclass-0x100001d10-0x7fff71092580-0x7fff71092580-0x100001d40
    NSLog(@"%s-%p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4);
   
}
void isKind_MemberOfClass(){

    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]];     //1,0,0,0
    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]];     //1,1,1,1
    NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
       
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        lgObjc_copyMethodList(pClass);

        lgInstanceMethod_classToMetaclass(pClass);
        lgClassMethod_classToMetaclass(pClass);
        lgIMP_classToMetaclass(pClass);
    }
    return 0;
}

2.问题解析

2.1 通过runtime的 class_copyMethodList获取方法列表验证方法的归属问题

  • 获取对象方法列表 对象方法:存放在类中,归属于
 unsigned int count = 0;
 Method *methods = class_copyMethodList(pClass, &count);
  • 获取类方法列表 类方法: 存放在元类,归属于元类,类是元类的实例
unsigned int count = 0;
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method *metalMethods = class_copyMethodList(metaClass, &count);

2.2 class_getInstanceMethod:分别在类和元类中找实例方法

对象方法:是类的实例方法,类方法:是元类的实例方法

class_getInstanceMethod(pClass,@selector(sayHappy))
class_getInstanceMethod(metalClass,@selector(sayHappy))
  • 类方法归属于元类,在类中获取实例方法,找不到类方法+sayHappy
  • 类是元类的实例,在元类中获取实例方法,可以找到类方法+sayHappy
class_getInstanceMethod(pClass,@selector(sayHello))
class_getInstanceMethod(metalClass,@selector(sayHello))
  • 实例方法归属于,在类中获取实例方法,可以到实例方法-sayHello
  • 类是元类的实例,在元类中获取实例方法,找不到实例方法-sayHello

2.3 class_getClassMethod 分别在类和元类中找类方法

class_getClassMethod(pClass, @selector(sayHello));
class_getClassMethod(metaClass, @selector(sayHello))
  • 实例方法归属于,在类中获取类方法,找不到实例方法-sayHello
  • 元类中获取类方法,找不到实例方法-sayHello
class_getClassMethod(pClass, @selector(sayHappy));
class_getClassMethod(metaClass, @selector(sayHappy));
  • 类方法归属于元类,在类中获取类方法,可以找到类方法+sayHappy
  • 元类中获取类方法,需要查看class_getClassMethod的底层实现 class_getClassMethod的底层是获取元类的class_getInstanceMethod方法。class_getInstanceMethod(cls->getMeta(),sel) 如果当前的类cls是元类,则cls->getMeta()不会继续往根元类递归,直接返回cls,在当前类(元类)cls中可以找到找到实例方法sayHappy

如果当前的类是元类,classgetClassMethod为啥不会继续往根元类递归?\color{#FF7D00}{如果当前的类是元类,class_getClassMethod为啥不会继续往根元类递归?}

通过isa的走位图,可知根元类的isa指向自己,如果元类查找类方法一直递归查找当前类的元类的实例方法,那么递归到根元类后会陷入死循环,所以OC底层在递归的时候,会判断当前类,如果当前类为元类,则不会继续递归,直接返回当前的类,在当前类(即元类)中查找实例方法;

3 isKindOfClass和isMemberOfClass

3.1 +isKindOfClass:从元类开始,递归父类,查询是否等于cls

  • 3.1.1 函数原型
+ (BOOL)isKindOfClass:(Class)cls {
	//从当前类的元类开始,递归父类,查询是否等于cls
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

//llvm底层优化: Calls [obj 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);
}
  • 3.1.2 面试题
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; 
    1. NSObject元类==NSObject--NO
    1. NSObject元类的父类(根元类) == NSObject --NO
    1. 根元类的父类(NSObject,即根类) == NSObject -- YES =>返回YES
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]]
    1. LGPerson元类==LGPerson--NO
    1. LGPerson元类的父类(NSObject的元类,即根元类) == LGPerson --NO
    1. 根元类的父类(NSObject,即根类) == LGPerson --NO
    1. 根类的父类 == nil 递归结束 => 返回NO

3.2 -isKindOfClass:从self的当前类开始,递归父类,查询是否等于cls

  • 3.2.1 函数原型:
- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
  • 3.2.2 面试题
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; 
 // NSObject==NSObject--YES
 
 
 BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];
    //LGPerson==LGPerson--YES

3.3 +isMemberOfClass 判断元类是否等于cls

  • 3.3.1 函数原型
+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}
  • 3.3.2 面试题
//+isMemberOfClass:判断元类是否等于cls
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     //NSObject元类(根元类) == NSObject --NO
  
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];//LGPerson元类==LGPerson--NO

3.4 -isMemberOfClass 判断self的类是否等于cls

  • 3.4.1 函数原型
- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
  • 3.4.2 面试题
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     // NSObject  == NSObject -- YES

BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]; //LGPerson == LGPerson -- YES