我已经阅读了很多关于覆盖init方法的不同帖子,希望找到一些我一直无法弄清楚的语法问题的答案。
(id) init
{
self = [super init];
if(self){
}
return self;
}
因此,当我们将init
方法发送到子类的超类(假设超类是NSObject
)时,我们正在初始化从超类继承的所有实例变量?这还有什么成就?
每当我们创建一个新类时,我们总是从父类继承实例变量吗?例如,如果我创建一个名为Fraction
...
Fraction : NSObject
Fraction * myFrac = [[Fraction alloc] init]
myFrac引用的对象是否会自动继承我尚未从父类声明的实例变量?
最后做的时候
self = [super init];
我们不是在初始化超类吗?我们究竟在自己存储什么? init的结果是指向我们子类的新初始化对象的指针吗?
我知道这已被问过很多次了,但我在解释中找不到这些答案。对不起堆积的问题。
答案 0 :(得分:4)
所以当我们将init方法发送到子类的超类时 (我们假设超类是NSObject)我们正在初始化所有 实例变量是从超类继承的吗?
默认情况下,所有ivars都设置为nil
/ NULL
/ 0
/ 0.0
/ NO
,具体取决于其类型,但您的父类可能我希望默认情况下将它们设置为其他内容,在这种情况下,它会在init
方法中更改其值。
这还有什么成就?
初始化新对象时,无论NSObject
(或您的父类)想要做什么。惯例基本上说,你不能使用一个尚未初始化的对象(release
除外) - 你可以释放一个从未被初始化的对象,这是明确允许的。大多数其他语言都知道contsructors的概念,例如在Java中你要说new String(...)
来创建一个字符串对象,它做两件事:它创建一个新的字符串对象,并通过调用它的构造函数初始化该对象。如果不做另一个,Java将不允许你做一件事。在Obj-C中,这两件事是个别步骤。 alloc
创建一个新对象,init
对其进行初始化。在某些情况下,提供两个单独的步骤具有优势,但它的缺点是必须依赖约定(必须在使用对象之前调用init
,但它永远不能被调用多次;编译器但是,至少在上次检查时,不会执行任何一项。
每当我们创建一个新类时,我们总是从父类继承实例变量吗?
是;除非NSObject
没有。 Obj-C中的大多数ivars都是私有的,受保护已经是一个很大的例外,你几乎看不到公共的。所以基本上你永远不应该直接访问父类的ivar,因此你不必关心你是否继承任何东西。
self = [super init];
我们不是在初始化超类吗?我们究竟存储了什么 自? init的结果是指向我们新初始化的指针 我们子类的对象?
允许init
方法返回与调用方法不同的对象。例如。以下是有效的:
static MyClass * ThereIsOnlyOneIstance;
- (id)init
{
if (ThereIsOnlyOneInstance) {
[self release];
return [ThereIsOnlyOneInstance retain]; // Without retain if using ARC
}
self = [super init];
if (!self) return nil;
ThereIsOnlyOneInstance = [self retain]; // Just `= self` if using ARC
return self;
}
以下两个if
- 陈述将成立:
MyClass a = [[MyClass alloc] init];
MyClass b = [MyClass alloc];
if (a != b) NSLog(@"a != b will be true");
b = [b init];
if (a == b) NSLog(@"Now a == b will be true");
同样,init
方法可能会失败,在这种情况下,它必须释放对象并返回nil
。因此,在调用[super init]
时,此方法可能会失败。不要过多考虑它为什么会失败,只要记住它可能会失败。现在假设您编写以下代码:
- (id)init
{
[super init]; // BAD!!! THIS IS BROKEN!!!
// Recent versions of CLANG will even make this
// a hard compiler error and refuse to compile that.
return self;
}
如果[super init]
失败,该对象已被释放并且已返回nil
,但您尚未更新self
,您只需返回{{1}中曾经使用过的任何值在调用self
之前。因此,您返回指向死对象的指针,因为在内存位置[super init]
指向的对象不再是对象,这是一个悬空指针并使用它可能会导致您的应用崩溃或其他故障。
这就是为什么你总是必须将另一个self
方法的输出写回给自己。从外部调用init
也是如此。以下代码被破坏:
init
它被破坏了,因为init可能释放对象MyClass x = [MyClass alloc];
[x init]; // BAD!!! THIS BROKEN!!!
指向,所以x
现在是一个悬空指针。您始终必须将init的输出捕获回应该指向该对象的变量。以下代码是正确的:
x
虽然通常只在一个组合呼叫中分配/ init,当然:
MyClass x = [MyClass alloc];
x = [x init];
但实际上是相同的,编译器生成的代码看起来没有什么不同。
答案 1 :(得分:2)
所以当我们将init方法发送到子类的超类时 (假设超类是NSObject)我们正在初始化所有 从超类继承的实例变量?还有什么呢? 完成?
没有。运行时将Objective-C上下文中的所有变量初始化为nil(而不是在C和C ++运行时没有显式初始化的垃圾值)。 -init
存在用于设置,实际上不需要NSObject的直接子类,因为默认的-init
方法返回self
并退出。也就是说,-init
及其族中的那些方法通常需要初始化成员变量并在继承链的下方设置对象的状态。不要认为它是+alloc
的伴侣,而只是一种方便的设置方法,它已成为该语言的常态。
myFrac引用的对象是否自动继承 我甚至还没有从父级声明的实例变量 类?
如果通过“继承”你的意思是你创建的任何变量仍然保持他们的超类提供的偏移,那么是。如果通过“继承”你的意思是“允许访问”,那么它取决于。 @public, @private
和@protected
指令确定派生类获取其父项的实例变量的访问权限。
我们不是在初级化超级班吗?
是的,但要明白init
和朋友实际上并没有分配内存,也没有设置任何语言特定的内容。他们只是设置,交出self
,然后走开。
我们究竟在自己存储什么?
我们存储由+alloc
分配的对象,并由NSObject以self
的形式返回给我们。调用super
只是让超类有机会运行它的设置,然后传回一个自我指针,这样我们就可以进行设置了。
init的结果是指向我们子类的新初始化对象的指针吗?