Runtime学习:面试题狙击
前面两篇文章分别记录了自己学习 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 指针和自己比较,是否相等。
- [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。
- isa 指向 NSObject 的 Meta Class,所以和 NSObject Class不相等。
- [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。
- isa 指向 Sark 的 Meta Class,和 Sark Class 也不等。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
[Android] 基于WLAN无线执行自动化方法
背景: 由于公司要测试APP 产品的耗电问题,我们采取的办法很low,对各个模块功能进行大量的手动测试,再通过Emmagee或GT得出来的结果来评估产品耗电,流量,CPU,内存的消耗等。由于手工效率太低,而且不准确,我们就决定用自动化来实现,但用自动化又面临了一个USB接电脑供电的问题,从而导致计算出来的功耗与手动跑的有很大的误差。 1、将 Android 设备和 adb 主计算机连接到这两者都可以访问的常用 WLAN 网络。注意,并非所有访问点均适用;可能需要使用已正确配置防火墙的访问点以支持 adb 的访问点。注:如果您尝试连接到 Android Wear 设备,则通过关闭与其连接的手机的蓝牙强制将它连接到 WLAN。 2、使用 USB 电缆将设备连接到主计算机。 3、设置目标设备以侦听端口 5555 上的 TCP/IP 连接。 didi@localhost ~ adb devices List of devices attached 68de2f65 device didi@localhost ~ adb tcpip 5555 restarting in TCP ...
- 下一篇
Android字符串反转、左移和右移
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点 反转 反转最简单的就是用StringBuilder和StringBuffer的reverse方法 private String reverseString(String original) { StringBuilder builder = new StringBuilder(); builder.append(original); return builder.reverse().toString(); } 左移和右移都有很多种方式来实现,这里简单介绍下三次反转法来实现左移右移 右移 /** * 右移index位 * @param from * @param index * @return */ private String rightMoveIndex(String from, int index) { from = reverseString(from); String first = from.substring(0,index); String second...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS关闭SELinux安全模块
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS8安装Docker,最新的服务器搭配容器使用