IOS底层原理 -5.运行时(2)

1. 面试题:isKindClass和isMemberOfClass

        NSLog(@"%d",[[NSObject class] isKindOfClass:[NSObject class]]);
        //这里最终比较的是NSObject类对象的metaClass是不是与其类对象是不是相同,结合类图所以结果不同
        NSLog(@"%d",[[NSObject class] isMemberOfClass:[NSObject class]]);
        NSLog(@"---------");
        //首先左边LYMPerson类对象的metaClass与右边LYMPerson类对象比较,如果不同继续比较左边LYMPerson类对象的metaClass的父类;结合类图结果不相同
        NSLog(@"%d",[[LYMPerson class] isKindOfClass:[LYMPerson class]]);
        // LYMPerson类对象的metaClass与其类对象比较,结合类图结果不相同
        NSLog(@"%d",[[LYMPerson class] isMemberOfClass:[LYMPerson class]]);
        NSLog(@"---------");
         // LYMPerson类对象的metaClass与NSObject比较,不同则继续比较 LYMPerson`类对象的metaClass的父类`,因为LYMPerson的最终父类就是NSObject, 结合类图因此结果相同
        NSLog(@"%d",[[LYMPerson class] isKindOfClass:[NSObject class]]);
        // LYMPerson类对象的metaClass与NSObject比较,结合类图结果不相同
        NSLog(@"%d",[[LYMPerson class] isMemberOfClass:[NSObject class]]);

在这里插入图片描述
想要研究上述问题得先看下下图:
在这里插入图片描述
先来开一下在objc的NSObject部分开源代码里这部分的实现:

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

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

从源码里可知:

  • 在实例方法中:isMemberOfClass只是比较实例方法的类对象是不是和传入的类是不是同一个;isKindOfClass是判断实例方法的类对象与传入的类进行比较,如果不相同 ,再和实例方法的类对象的父类进行比较,一层一层比较;
  • 在类方法中:isMemberOfClass只是拿到类对象的metaClass去判断是不是等于传入的类对象;isKindOfClass是拿到类对象的metaClass与传入的类进行比较,如果不相同 ,再和类对象的原类对象的父类进行比较,一层一层比较;

综上所述。如果需要全部输出正确,上述代码修改为:

#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"%d",[NSObject isKindOfClass:[NSObject class]]);
        NSLog(@"%d",[NSObject isMemberOfClass:object_getClass([NSObject class])]);
        NSLog(@"---------");
        NSLog(@"%d",[LYMPerson isKindOfClass:object_getClass([LYMPerson class])]);
        NSLog(@"%d",[LYMPerson isMemberOfClass:object_getClass([LYMPerson class])]);
        NSLog(@"---------");
        NSLog(@"%d",[LYMPerson isKindOfClass:[NSObject class]]);
        NSLog(@"%d",[[LYMPerson superclass] isMemberOfClass:object_getClass([NSObject class])]);
        
    }
    return 0;
}

修改后的输出如下图:
在这里插入图片描述
上述面试题的每个输出结果的分析,在每行代码的上面注释部分说明

面试题:类的底层原理和方法调用原理

推荐一个很好的博客:神经病院 Objective-C Runtime 入院第一天—— isa 和 Class

#import "LYMPerson.h"

@implementation LYMPerson
-(void)showName{
    NSLog(@"This name is %@",self.userName);
}
@end
#import "ViewController.h"
#import "LYMPerson.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    id person = [LYMPerson class];
    void *cls = &(person);
    [(__bridge id)cls showName];
}


@end

最终输出:
在这里插入图片描述
下面从两个方面分析

1. 为什么可以输出?

  • 任何一个OC的对象在编译成C++后的结构体中都会有一个isa指针,父类的属性,自己的属性;方法的实现存储在其类对象中,实例对象通过ISA可以找到类对象;

2. 输出为什么是viewController中的?

先看一下内存分布:
在这里插入图片描述
内存中最高位的是ViewController,LYMPerson在相对最低位;
增加NSString 后的内存分布:

在这里插入图片描述
内存中大致图:
在这里插入图片描述
首先使用iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-12.0.0 LYMPerson.m重写成c++代码:

struct NSObject_IMPL {
	__unsafe_unretained Class isa;
};
struct LYMPerson_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	NSString * _Nonnull __strong _userName;
};
  • 正常情况下如果需要使用_userName,那么类的isa指针需要移动除过自己的8位开始查找,直到找到第一个停止;上述面试题中cls相当于isa指针,那个需要找到_userName输出,就需要移动除过自己的8位开始查找,在上面图片中的内存结构中刚好找到name变量,就直接输出了name的值;同样对于第一个示例代码中的内存分布中下8位刚好是viewController类;

总结:这道面试题知识点:objc类的底层实现、局部变量在栈中的分布、方法调用的底层原理、

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章