Runtime学习:面试题狙击

简介: 前面两篇文章分别记录了自己学习 Runtime 的一些知识点以及常见的一些应用。之前立下 flag 说准备写三篇关于 Runtime 的文章,于是就有了这篇文章。题目一:下面的代码输出什么?@implementation Son : Father- (id)init { self =...

前面两篇文章分别记录了自己学习 Runtime 的一些知识点以及常见的一些应用。之前立下 flag 说准备写三篇关于 Runtime 的文章,于是就有了这篇文章。

题目一:下面的代码输出什么?

@implementation Son : Father
- (id)init {
    self = [super init];
    if (self) {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}
@end

结果: Son / Son

分析:

对于上面的答案,第一个的结果应该是我们的预期结果,但是第二个结果却让我们很费解了。

那我们利用前面文章讲过的知识点来分析一下整个的流程。

因为,Son 及 Father 都没有实现 -(Class)calss 方法,所以这里所有的调用最终都会找到基类 NSObject 中,并且在其中找到 -(Class)calss 方法。那我们需要了解的就是在 NSObject 中这个方法的实现了。

在 NSObject.mm 中可以找到 -(Class)class 的实现:

- (Class)class {
    return object_getClass(self);
}

在 objc_class.mm 中找到 object_getClass 的实现:

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

ps:上面的方法定义可以去官方OpenSource中下载源码哦。

可以看到,最终这个方法返回的是,调用这个方法的 objc 的 isa 指针。那我们只需要知道在题干中的代码里面最终是谁在调用 -(Class)class 方法就可以找到答案了。

接下来,我们利用 clang -rewrite-objc 命令,将题干的代码转化为如下代码:

NSLog((NSString *)&__NSConstantStringImpl__var_folders_8k_cgm28r0d0bz94xnnrr606rf40000gn_T_Car_3f2069_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_8k_cgm28r0d0bz94xnnrr606rf40000gn_T_Car_3f2069_mi_1, NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Car"))}, sel_registerName("class"))));

从上方可以得出,调用 [Father class] 的时候,本质是在调用

objc_msgSendSuper(struct objc_super *super, SEL op, ...)

struct objc_super 的定义如下:

struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained _Nonnull id receiver;

    /// Specifies the particular superclass of the instance to message. 
#if !defined(__cplusplus)  &&  !__OBJC2__
    /* For compatibility with old objc-runtime.h header */
    __unsafe_unretained _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
    /* super_class is the first class to search */
};

从定义可以得知:当利用 super 调用方法时,只要编译器看到super这个标志,就会让当前对象去调用父类方法,本质还是当前对象在调用,是去父类找实现,super 仅仅是一个编译指示器。但是消息的接收者 receiver 依然是self。最终在 NSObject 获取 isa 指针的时候,获取到的依旧是 self 的 isa,所以,我们得到的结果是:Son。

扩展一下: 看看下方的代码会输出什么?

@interface Father : NSObject
@end

@implementation Father

- (Class)class {
    return [Father class];
}

@end

---

@interface Son : Father
@end

@implementation Son

- (id)init {
    self = [super init];
    if (self) {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}

@end

int main(int argc, const char * argv[]) {
    Son *foo = [[Son alloc]init];
    return 0;
}

---输出:---
Father
Father
    • *

更多的面试题和答案:https://github.com/iOSputao/iOS-

题目二:以下的代码会输出什么结果?

@interface Sark : NSObject
@end
@implementation Sark
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...

        NSLog(@"%@", [NSObject class]);
        NSLog(@"%@", [Sark class]);

        BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
        BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
        BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
        BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];
        NSLog(@"%d--%d--%d--%d", res1, res2, res3, res4);
    }
    return 0;
}

结果: 1--0--0--0

分析:

首先,我们先去查看一下题干中两个方法的源码:

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

可以得知:

  • isKindOfClass 的执行过程是拿到自己的 isa 指针和自己比较,若不等则继续取 isa 指针所指的 super class 进行比较。如此循环。
  • isMemberOfClass 是拿到自己的 isa 指针和自己比较,是否相等。
  1. [NSObject class] 执行完之后调用 isKindOfClass,第一次判断先判断 NSObject 和 NSObject 的 meta class 是否相等,之前讲到 meta class 的时候放了一张很详细的图,从图上我们也可以看出,NSObject 的 meta class 与本身不等。接着第二次循环判断 NSObject 与meta class 的 superclass 是否相等。还是从那张图上面我们可以看到:Root class(meta) 的 superclass 就是 Root class(class),也就是 NSObject 本身。所以第二次循环相等,于是第一行 res1 输出应该为YES。
  2. isa 指向 NSObject 的 Meta Class,所以和 NSObject Class不相等。
  3. [Sark class] 执行完之后调用 isKindOfClass,第一次 for 循环,Sark 的 Meta Class 与 [Sark class] 不等,第二次 for 循环,Sark Meta Class 的 super class 指向的是 NSObject Meta Class, 和 Sark Class 不相等。第三次 for 循环,NSObject Meta Class 的 super class 指向的是 NSObject Class,和 Sark Class 不相等。第四次循环,NSObject Class 的super class 指向 nil, 和 Sark Class 不相等。第四次循环之后,退出循环,所以第三行的 res3 输出为 NO。
  4. isa 指向 Sark 的 Meta Class,和 Sark Class 也不等。
目录
相关文章
|
6月前
|
设计模式 前端开发 算法
No210.精选前端面试题,享受每天的挑战和学习
No210.精选前端面试题,享受每天的挑战和学习
No210.精选前端面试题,享受每天的挑战和学习
|
25天前
|
算法 前端开发 Java
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
这篇文章总结了单链表的常见面试题,并提供了详细的问题分析、思路分析以及Java代码实现,包括求单链表中有效节点的个数、查找单链表中的倒数第k个节点、单链表的反转以及从尾到头打印单链表等题目。
28 1
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
|
22天前
|
Java 应用服务中间件 程序员
JVM知识体系学习八:OOM的案例(承接上篇博文,可以作为面试中的案例)
这篇文章通过多个案例深入探讨了Java虚拟机(JVM)中的内存溢出问题,涵盖了堆内存、方法区、直接内存和栈内存溢出的原因、诊断方法和解决方案,并讨论了不同JDK版本垃圾回收器的变化。
23 4
|
1月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
86 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
6月前
|
设计模式 缓存 Dart
Flutter学习笔记&学习资料推荐,15分钟的字节跳动视频面试
Flutter学习笔记&学习资料推荐,15分钟的字节跳动视频面试
|
2月前
|
网络协议 算法 数据库
|
5月前
|
前端开发 Java C#
GitHub突破5k Star!这件事情我坚持了3年,努力打造C#/.NET/.NET Core全面的学习、工作、面试指南知识库
GitHub突破5k Star!这件事情我坚持了3年,努力打造C#/.NET/.NET Core全面的学习、工作、面试指南知识库
|
6月前
|
机器学习/深度学习 数据挖掘 开发工具
2024年最全0基础学python开发工具及学习平台推荐_python平台a,面试阿里巴巴客服
2024年最全0基础学python开发工具及学习平台推荐_python平台a,面试阿里巴巴客服
2024年最全0基础学python开发工具及学习平台推荐_python平台a,面试阿里巴巴客服
|
6月前
|
Java 程序员 C语言
2024年Python最新【Python学习教程】Python类和对象_python中类和对象的讲解,Python最新面试题
2024年Python最新【Python学习教程】Python类和对象_python中类和对象的讲解,Python最新面试题
2024年Python最新【Python学习教程】Python类和对象_python中类和对象的讲解,Python最新面试题
|
6月前
|
前端开发 JavaScript 程序员
async-validator 源码学习(一):文档翻译,2024年最新如何面试大厂
async-validator 源码学习(一):文档翻译,2024年最新如何面试大厂