2

我已经阅读了很多关于重写 init 方法的不同帖子,希望能找到一些我一直无法弄清楚的语法问题的答案。

(id) init
{
    self = [super init];
    if(self){

    }
    return self;
}

因此,当我们将init方法发送到子类的超类(假设超类是NSObject)时,我们正在初始化从超类继承的所有实例变量?这还有什么作用?

每当我们创建一个新类时,我们总是从父类继承实例变量吗?例如,如果我创建一个名为Fraction...

Fraction : NSObject
Fraction * myFrac = [[Fraction alloc] init]

myFrac 引用的对象是否会自动从父类继承我尚未声明的实例变量?

最后做的时候

self = [super init];

我们不是在初始化超类吗?我们到底在 self 中存储了什么?init 的结果是指向我们新初始化的子类对象的指针吗?

我知道这已经被问过很多次了,但我在解释中找不到这些答案。抱歉,问题堆积如山。

4

2 回答 2

4

因此,当我们将 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];

但这实际上是一样的,编译器生成的代码看起来与以前没有什么不同。

于 2013-07-30T12:26:32.410 回答
2

因此,当我们将 init 方法发送到子类的超类(假设超类是 NSObject)时,我们正在初始化从超类继承的所有实例变量?这还能完成什么?

不会。运行时会为您将 Objective-C 上下文中的所有变量初始化为 nil(而不是在 C 和 C++ 运行时下没有显式初始化的垃圾值)。 -init为设置而存在,对于 NSObject 的直接子类实际上是不必要的,因为默认-init方法返回self并退出。也就是说,-init其家族中的那些方法通常是初始化成员变量和设置对象状态的必要条件。不要将其视为 的伴侣+alloc,而只是一种方便的设置方法,已成为该语言的规范。

myFrac 引用的对象是否会自动从父类继承我尚未声明的实例变量?

如果通过“继承”你的意思是你创建的任何变量仍然保持他们的超类给他们的偏移量,那么是的。如果“继承”是指“授予访问权限”,则视情况而定。@public, @private@protected指令确定派生类对其父类的实例变量的访问权限。

我们不是在初始化超类吗?

是的,但要明白,init朋友们实际上并没有分配内存,也没有设置任何特定于语言的东西。他们只是设置,交出self,然后走开。

我们到底在 self 中存储了什么?

我们将+allocNSObject 分配并返回给我们的对象以self. 调用 tosuper只是让超类有机会运行它的设置,然后将一个 self 指针传回给我们,这样我们就可以进行设置了。

init 的结果是指向我们新初始化的子类对象的指针吗?

哦,我当然希望如此

于 2013-07-30T04:39:48.447 回答