因此,当我们将 init 方法发送到子类的超类(假设超类是 NSObject)时,我们正在初始化从超类继承的所有实例变量?
默认情况下,所有 ivars 都设置为nil
/ NULL
/ 0
/ 0.0
/ NO
,具体取决于它们的类型,但您的父类可能希望将它们默认设置为其他值,在这种情况下,它将在其init
方法中更改它们的值。
这还有什么作用?
NSObject
初始化新对象时(或您的父类)想要做什么。基本上约定说,你不能使用一个没有被初始化的对象(除了release
- 你可以释放一个从未被初始化的对象,这是明确允许的)。大多数其他语言都知道构造函数的概念,例如在Java 中,您会说new String(...)
创建一个字符串对象,它会做两件事:它创建一个新的字符串对象,并通过调用其构造函数来初始化该对象。Java 不允许你只做一件事而不做另一件事。在 Obj-C 中,这两件事是单独的步骤。alloc
创建一个新对象并init
初始化它。提供两个单独的步骤在某些情况下有好处,但它也有缺点,您必须依赖约定(init
必须在使用对象之前调用,但不能多次调用;编译器不会强制执行任何一个,不过,至少不是上次我检查过的)。
每当我们创建一个新类时,我们总是从父类继承实例变量吗?
是的; 除非NSObject
没有。Obj-C 中的大多数 ivars 都是私有的,protected 已经是一个巨大的例外,你几乎看不到公共的。所以基本上你不应该直接访问你的父类的 ivar,因此你不必关心你是否继承了任何或没有。
self = [超级初始化];
我们不是在初始化超类吗?我们到底在 self 中存储了什么?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
,您只需返回self
调用之前的任何值[super init]
。结果,您返回一个指向死对象的指针,因为在内存位置self
指向的对象不再是对象,这是一个悬空指针,使用它可能会导致您的应用程序崩溃或发生其他故障。
这就是为什么你总是必须将另一个init
方法的输出写回自己。init
从外部呼叫也是如此。以下代码已损坏:
MyClass x = [MyClass alloc];
[x init]; // BAD!!! THIS BROKEN!!!
它被破坏了,因为 init 可能会释放x
指向的对象,所以x
现在是一个悬空指针。您始终必须将 init 的输出捕获回应该指向对象的变量。以下代码是正确的:
MyClass x = [MyClass alloc];
x = [x init];
虽然通常你只在一个组合调用中分配/初始化,当然:
MyClass x = [[MyClass alloc] init];
但这实际上是一样的,编译器生成的代码看起来与以前没有什么不同。