Runtime系列:super调用函数本质、isMemberOfClass与isKindOfClass的区别、综合分析【05】

1、super调用函数本质

class 方法的底层实现:

// 获取调用者对象的类
- (Class)class {
    
    
    return object_getClass(self);
}

// 获取调用者对象的夫类
- (Class)superclass {
    
    
    return class_getSuperclass(object_getClass(self));
}

super的底层实现【[super message]的底层实现】:

1、消息接收者仍然是子类对象。
2、从父类开始查找方法的实现。

示例:

#import "Person.h"

@interface Student : Person

@end
#import <Foundation/Foundation.h>

@interface Person : NSObject
- (void)run;
@end
- (instancetype)init
{
    
    
    if (self = [super init]) {
    
    
        NSLog(@"[self class] = %@", [self class]); // Student
        NSLog(@"[self superclass] = %@", [self superclass]); // Person

        NSLog(@"--------------------------------");

        /**
        * super调用实现本质:
        * objc_msgSendSuper({self, [Person class]}, @selector(class));
        */
        NSLog(@"[super class] = %@", [super class]); // Student
        NSLog(@"[super superclass] = %@", [super superclass]); // Person
    }
    return self;
}

@end

2、isMemberOfClass、isKindOfClass区别

先看下底层逻辑:

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

- (BOOL)isKindOfClass:(Class)cls {
    
    
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
    
    
        if (tcls == cls) return YES;
    }
    return NO;
}


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


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

示例1:

        id person = [[MJPerson alloc] init];
        // 判断左边的实例对象是否等于右边的类
        NSLog(@"%d", [person isMemberOfClass:[MJPerson class]]); // 1
        NSLog(@"%d", [person isMemberOfClass:[NSObject class]]); // 0

        // 判断左边的实例对象是否属于右边的类或子类
        NSLog(@"%d", [person isKindOfClass:[MJPerson class]]); // 1
        NSLog(@"%d", [person isKindOfClass:[NSObject class]]); // 1

示例2:

        NSLog(@"%d", [NSObject isKindOfClass:object_getClass([NSObject class])]); // 1
        NSLog(@"%d", [NSObject isMemberOfClass:object_getClass([NSObject class])]); // 1
        NSLog(@"%d", [NSObject isKindOfClass:object_getClass([MJPerson class])]); // 0
        NSLog(@"%d", [MJPerson isMemberOfClass:object_getClass([MJPerson class])]); // 1
        
        // -------
        
        // 这句代码的方法调用者不管是哪个类(只要是NSObject体系下的),都返回YES
        NSLog(@"%d", [NSObject isKindOfClass:[NSObject class]]); // 1
        NSLog(@"%d", [NSObject isMemberOfClass:[NSObject class]]); // 0
        NSLog(@"%d", [MJPerson isKindOfClass:[MJPerson class]]); // 0
        NSLog(@"%d", [MJPerson isMemberOfClass:[MJPerson class]]); // 0

总结:
1、实例方法是类的判断:

1、isMemberOfClass:判断左边的实例对象是否等于右边的类
2、isKindOfClass:判断左边的实例对象是否属于右边的类或子类

2、类方法是元类的判断:

1、正常情况下传参应该取元类对象进行判断断。
2、如果是传类对象判断,返回全部为0;除非右边入参为 [NSObject class],此时方法调用者不管是哪个类(只要是NSObject体系下的),都返回YES。

3、以下代码能不能执行成功?如果可以,打印结果是什么?

在这里插入图片描述
考察知识点:
1、super 调用的本质
2、函数栈空间的分配问题
3、消息机制
4、访问成员变量的本质

1、指针分析

1、此时 cls 存放的是 MJPerson类的地址
2、obj 是MJPerson类的地址的指针
3、MJPerson.name 是 MJPerson 结构体中的成员变量,位于isa的下一个栈中。
4、因为MJPerson 没有初始化,所以实际情况是 name 属性没有被转成员变量,成为堆中的一个变量。
5、因此,此时 self.name 取的值是 MJPerson结构体isa的下一个栈信息。

在这里插入图片描述

2、[super viewDidLoad]本质分析

底层结构:

    struct abc = {
    
    
        self,
        [ViewController class]
    };
    objc_msgSendSuper2(abc, sel_registerName("viewDidLoad"));

从这个底层结构可知,MJPerson.isa 的下一个地址是self、再下一个是 [ViewController class]。那么其堆地址信息如下:

3、堆栈分析

在这里插入图片描述

4、打印取值

- (void)print
{
    
    
    NSLog(@"my name is %@", self.name);
}

self.name 实际是取MJPerson结构体中isa的下一个地址【self->_name】,而MJPerson结构体中isa的下一个地址是ViewController中的self的实例对象。

综上分析,最终print打印出来的值是 self 对象。

验证:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_38633659/article/details/124874626