OC
项目, 定义Person
类, 添加name
属性和实现print
方法, 以下图所示ViewController
中的-viewDidLoad
方法里实现下面的代码问: 代码是否会报错? 是否可以顺利运行? 是否能打印?, 若是能, 会打印什么?面试
Person name is <ViewController: 0x7ff78ef08880>
复制代码
问: 为何这么打印?bash
person
是一个指针, 存储着[[Person alloc] init]
的地址, 因此person
指向刚建立的Person
实例对象isa
, 因此[[Person alloc] init]
在底层就是下面的样子isa
是Person
对象中的第一个成员变量, 因此isa
的地址与[[Person alloc] init]
的地址相同person
调用-print
方法时, 本质是经过isa
找到[Person class]
对象, 而后找到-print
方法来调用, 因此指针关系图以下-print
方法中, 有打印成员变量_name
, 因此须要经过[[Person alloc] init]
找到_name
存储的值isa的地址 + 8
找到_name
的地址, 而后访问_name
存储的内容obj
能调用print
方法的缘由id cls = [Person class];
复制代码
cls
指针指向Person
的类对象obj
存储着cls
的地址void *obj = &cls;
复制代码
obj
指向cls
person
指向相似, 有下面这种指针结构[(__bridge id)obj print];
复制代码
在结构上ui
person
调用方法的流程是: person->isa->[Person class]
obj
调用的流程与person
相似. obj->cls->[Person class]
这就是obj
可以调用-print
方法的缘由spa
0x7ffeeec0c9d8
0x7ffeeec0c9d0
0x7ffeeec0c9c8
复制代码
能够看到, 连续的三个局部变量, 在栈中的存储顺序是连续的3d
接着咱们看下面的这种状况, 在cls
前面添加test
变量, 运行看一下打印指针
obj
经过找到cls
, 而后用cls的地址+8
, 找到的就是test
的地址, 因此打印时, 找到的_name
就是test
的值123
那么面试题中的打印为何是
ViewController
的实例对象呢?code
ViewController.cpp
底层结构[super viewDidLoad];
这一句在底层代码是下面这一句((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
复制代码
objc_msgSendSuper({self, class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
复制代码
{self, class_getSuperclass(objc_getClass("ViewController"))}
cls的地址+8
, 找到的就是self
, 也就是当前控制器对象