美文网首页
isKindOfClass isMemberOfClass

isKindOfClass isMemberOfClass

作者: 曹来东 | 来源:发表于2018-09-06 14:01 被阅读16次

查看NSObject.mm源码

+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

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

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
  • isKindOfClass:是否是同类即子类
  • isMemberOfClass:是否是同类

两个方法的正确用法:

1.对象方法:
左侧参数的class对象是否是右侧参数的class类型
即左侧如果传入instance对象,右侧参数传入的应该是class对象.
2.类方法
左侧参数的meta-class对象是否是右侧参数的meta-class类型.即左侧如果传入class对象,右侧应该传入meta-class对象.
正确用法

        LDPerson * person =  [[LDPerson alloc] init];
    获取instance对象person的class对象,和右侧的class对象比较
        NSLog(@"%d",[person isKindOfClass:[NSObject class]]);//1
        NSLog(@"%d",[person isMemberOfClass:[NSObject class]]);//0
    获取class对象LDPerson的meta-class对象,和右侧的meta-class对象比较
        NSLog(@"%d",[LDPerson isKindOfClass:object_getClass([LDPerson class])]);//1
        NSLog(@"%d",[LDPerson isMemberOfClass:object_getClass([LDPerson class])]);//1

下面打印结果

    NSLog(@"%d",[[NSObject class] isKindOfClass:[NSObject class]]);//1
    NSLog(@"%d",[[NSObject class] isMemberOfClass:[NSObject class]]);//0
    NSLog(@"%d",[[LDPerson class] isKindOfClass:[LDPerson class]]);//1
    NSLog(@"%d",[[LDPerson class] isMemberOfClass:[LDPerson class]]);//0
    //等价写法如下
    //获取class对象的meta-class ,是否是右侧的meta-class类型
    //但是后面都传入的是class对象.使用方法很奇怪,一般不要这样写.
    //NSObject的meta-class 不等于NSObject的class对象.然后会通过superclass指针
    //拿到NSObject的meta-class的superclass:是NSObject的class对象,所以是1.
    NSLog(@"%d",[NSObject isKindOfClass:[NSObject class]]);//1
    //NSObject的meta-class 不等于NSObject的class对象
    NSLog(@"%d",[NSObject isMemberOfClass:[NSObject class]]);//0
    //LDPerson的meta-class 不等于[LDPerson class]//最后找到NSObject,然后是[NSObject class]
    NSLog(@"%d",[LDPerson isKindOfClass:[LDPerson class]]);//0
    //LDPerson的meta-class 不等于[LDPerson class]
    NSLog(@"%d",[LDPerson isMemberOfClass:[LDPerson class]]);//0

打印结果是什么

@interface LDPerson : NSObject
@property (nonatomic,strong) NSString * name;
- (void)print;
@end
@implementation LDPerson
- (void)print{
    NSLog(@"%@",self.name);
}
- (void)viewDidLoad {
    [super viewDidLoad];
//没有这句代码打印的是控制器
//有这句代码打印的是@"123"
     NSString * str = @"123";
    id cls = [LDPerson class];
    void * obj = &cls;
    [(__bridge id)obj print];
}
@end

为什么能调用成功

image.png
image.png image.png
  • instcane对象调用方法是通过instcane对象的isa查找到class对象,然后遍历class对象的缓存 方法列表 superclass等查找方法.本质是查找class对象里的方法.
  • cls指向class对象,相当于instance对象的isa指向class对象.所以obj相当于instance对象的指针.cls相当于instance对象.cls的第一个成员就是isa,所以整合代码:
id obj = cls([[LDPerson alloc] init]);
[obj print];

为什么打印结果是栈空间的第一个成员

LDPerson的底层结构

struct LDPerson_IMPL{
  Class isa;
  NSString *_name;
}

所以获取_name就是忽略前面isa所占用的八个字节,来取值.obj当做instance对象指针使用,obj指向cls,cls即是instance对象内存空间也是instance对象的isa.所以取值就是isa前面分配的内存.就是cls前面的.

为什么是控制器

[super viewDidLoad]底层生成代码:

objc_msgSendSuper({
  self;
  [UIViewController class]
},sel_registerName(""viewDidLoad));
//第一个参数是一个结构体,是一个栈上的局部结构体变量
struct superStruct = {
  self;
  [UIViewController class]
}
//第二个参数是函数调用.
sel_registerName(""viewDidLoad)
//所以转换后的底层代码为:第一个参数是结构体变量,第二个参数是SEL
objc_msgSendSuper(superStruct,sel_registerName(""viewDidLoad))

所以此时栈结构是:(上面是高地址)

[UIViewController class]
self
cls
obj

取值是obj上买八个字节之后的位置,即cls上面的self.即为控制器对象.

Super本质

相关文章

网友评论

      本文标题:isKindOfClass isMemberOfClass

      本文链接:https://www.haomeiwen.com/subject/dddtgftx.html