id cls = [Cls class];
[super viewDidLoad];
当我们进行这部操作后,会发现,执行speak方法时崩溃了,错误是EXC_BAC_ACCESS,说明是我们引用野指针了。
由此也可以证实,[super viewDidLoad];肯定做了一些骚操作,将ViewController的self压入了栈区。
接下来我们就需要探究究竟做了什么操作,我们可以用如下的命令行代码将ViewController.m重写成c++代码,然后观看发生了什么。
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc ViewController.m -o ViewController.cpp
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
我们可以发现原本这个方法里面会传入两个参数一个是self,一个是_cmd,当我们调用[super viewDidLoad]时,执行的方法中传入了参数self,由此将self做为一个值压入了栈中,但是_cmd这个参数并未被使用,因此,没有被压入栈中。
至此,这个问题已经被解释出来了。
答案
所有NSObject对象的首地址都是指向这个对象的所属类。这个条件是充要条件。反过来说,如果一个地址指向某个类,我们就可以把这个地址当成对象去用。所以编译是会通过的,也不会报unrecognized selector的错误。
打印结果会是ViewController对象的原因是因为cls在栈上的数据结构符合了它作为真实的类时候的数据结构,cls.name原本地址正好是栈上ViewController对象地址,因此NSLog能打印出<ViewController >
思索










