iOS 子类初始化方法中 为什么要使用 self = [super init] 对self进行赋值
self = [super init] 这个问题一直不太明白,今天研究了一下,在stackoverflow找到了下面的答案:
http://stackoverflow.com/questions/2956943/why-should-i-call-self-super-init
我对这些答案简单翻译总结下:
要明白这个问题,首先要知道self 是什么东西,我们什么时候会用到self。
self指针,不是在实例中的保存的真实指针,而是一个隐藏的函数参数。比如我们调用 [person getName]; 时,系统会翻译为
id objc_msgSend(id theReceiver, SEL theSelector, ...)
这种调用,其中的 theReceiver,就是我们在实例对象的方法(这里就是getName方法)里可以用到的self,其实它仅仅是一个隐藏的函数参数。
看完了self是什么,在看看super是什么,这里,可以参见这篇博文,写得非常出色:
http://chun.tips/blog/2014/11/05/bao-gen-wen-di-objective[nil]c-runtime(1)[nil]-self-and-super/
我把重点说一下:
调用如下代码:
NSLog(@"%@", NSStringFromClass([self class])); NSLog(@"%@", NSStringFromClass([super class]));
使用 clang -rewrite-objc 这个命令后,得到的中间代码有如下关键部分:
(void *)objc_msgSend)((id)self, sel_registerName("class")
(void *)objc_msgSendSuper)((__rw_objc_super){ (id)self, (id)class_getSuperclass(objc_getClass("Son")) }, sel_registerName("class")
注意,用了super去调用,其实还是把self的值传递进去了,但是,多传递了一个类名,这里就是“Son”,并且使用了objc_msgSendSuper进行消息传递。
简单地说,我们在方法中使用到的 super和self 都是在函数调用时,隐藏传递进去的一个参数,但是,super会从父类方法列表中先进行索引,如果找到了函数实现,就不再调用子类的函数实现了。
其实,super和self真正指向的都是已经被分配内存的对象,在 init方法中,就是alloc后,没有初始化的一段内存空间。[super init] 就是把这块内存空间先传递给父类,去父类调用父类的init方法,返回一段部分初始化好内存空间。为了方便理解,我画了一个大概的图:
在[super init] 之后,父类的变量应该被初始化完毕,而子类的变量还没有初始化。
下面继续正题:
我们如果继承NSObject类,不需要写这个 self = [super init],因为 [super init] 在一般情况下作用,仅仅是把原有的内存初始化一下,再把原来是self地址返回,内存地址没有变化,self 重新覆值没有意义。
但是以下2种情况除外:
1. 内存分配出错,那么 [super init] 会返回空,表明错误,这时,如果不把 self 设置为nil,而是继续指向alloc后的那块内存,再访问个指针就会产生问题。
2. 还有一种特殊情况,叫做Class Cluster,翻译中文的话,类族,官方文档 https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaEncyclopedia/ClassClusters/ClassClusters.html
再看一篇好的博文:
http://blog.sunnyxx.com/2014/12/18/class-cluster/
按照博客里的方法,我也实验了一下,截图如下:
obj1 , obj3 不但类型不同,而且地址也不同!我如果继承了NSArray(会这样操作吗?没有实验),并且重写了init方法,那么必须在init中,明确写出 self = [super init],不然,就根得不到正确类型的对象。发生这个问题的根本原因,在objective-c里,init方法是可以有返回值的, 这样导致 init方法不单单可以使用alloc方法所产生的内存去初始化,还可以在init函数内部重新分配一块内存,初始化后返回。这种奇特的写法被大家所认可,成为共识。在你不知道你继承的父类是否进行了这种特殊操作时,最好在子类初始化时使用 self = [super init], 重新对self 进行赋值,来确保你写的子类是正确的。