2

Stephen Kochan の本 "Programming in Objective-C" - 第 4 版の次の内容を理解するのに苦労しています。ご理解いただけると幸いです。

第 10 章の「オブジェクトの初期化」セクションで、Stephen は次のように書いています。

イニシャライザを記述するときは、次の 2 つの戦略に従う必要があります。

クラス内のオブジェクトの 1 つが初期化されるたびに、何か特別なことをしたい場合があります。たとえば、クラスが使用し、1 つ以上のインスタンス変数を介して参照するオブジェクトを作成するのに最適な場所です。その完璧な例は、Rectangle クラスです。init メソッドで四角形の XYPoint 原点を割り当てるのが合理的です。そのためには、継承された init メソッドをオーバーライドするだけです。

init をオーバーライドするために使用される標準の「テンプレート」があり、次のようになります。

- (id) init
{
     self = [super init];
     if (self) {
          // initialisation code here.
     }

     return self;
}

このメソッドは、最初に親初期化子を呼び出します。親の初期化子を実行すると、継承されたインスタンス変数が適切に初期化されます。

親の init メソッドを実行した結果を自分自身に割り当てる必要があります。初期化子にはメモリ内のオブジェクトの場所を変更する権利があるためです (つまり、その参照が変更されます)。

親の初期化子が成功した場合、返される値は、if ステートメントでテストされているように非 nil になります。コメントが示すように、次のブロック内に、オブジェクトの独自のカスタム コードを配置できます。これには、多くの場合、クラスにあるインスタンス変数の割り当てと初期化が含まれます。

OK、これまでのところ、Stephen Kochan が言おうとしていることは理解できましたが、次の部分では完全に当​​惑しています。お役に立てれば幸いです。

クラスに複数の初期化子が含まれている場合、そのうちの 1 つを指定された初期化子にし、他のすべての初期化メソッドでそれを使用する必要があります。通常、これは最も複雑な初期化方法です (通常、最も多くの引数を取る方法です)。

したがって、私の最初の質問は次のとおりです。この場合、「指定された」イニシャライザをすべて使用する場合、他のすべての初期化メソッドがあるのはなぜですか?

Stephen Kochan 氏は次のように続けています。

指定された初期化子を作成すると、メインの初期化コードが 1 つのメソッドに集約されます。クラスをサブクラス化する人は誰でも、指定された初期化子をオーバーライドして、新しいインスタンスが適切に初期化されるようにすることができます。

この例を挙げていただけますか?彼の言っていることが理解できたのかよくわからない.

スティーブンは続けます:

その議論に基づいて、 Fraction クラスの初期化メソッド initWith:over: は次のようになります。

- (Fraction *) initWith:(int)n over:(int)d
{
    self = [super init];

    if (self) {
        [self setTo: n over: d];
    }

    return self;
}

super の初期化 (およびその成功は、ゼロ以外の値の戻りによって示される) に続いて、setTo:over:メソッドを使用して Fraction の分子と分母を設定します。他の初期化メソッドと同様に、ここで行う初期化されたオブジェクトを返すことが期待されています。

initWith:over:プログラム 10.1 は、新しい初期化方法をテストします。

#import "Fraction.h"

int main (int argc, char *argv[])
{
    @autoreleasepool {
        Fraction *a, *b;

        a = [[Fraction alloc] initWith: 1 over: 3];
        b = [[Fraction alloc] initWith: 3 over: 7];

        [a print];
        [b print];

    }

    return 0;
}

出力:
1/3
3/7

これまでのところ、コードを理解しました。次の部分がまったくわかりません。

指定されたイニシャライザについて前述した規則に従うには、 Fraction クラスの init も変更する必要があります。クラスがサブクラス化される可能性がある場合、これは特に重要です。

init メソッドは次のようになります。

- (id)init
{
    return [self initWith:0 over:0];
}

サブクラス化する場合、なぜこれが重要なのですか?

スティーブン・コーチャンは次のように続けます。

プログラムが実行を開始すると、初期化呼び出しメソッドがすべてのクラスに送信されます。クラスと関連するサブクラスがある場合、親クラスが最初にメッセージを取得します。このメッセージは各クラスに 1 回だけ送信され、他のメッセージがクラスに送信される前に送信されることが保証されます。その目的は、その時点でクラスの初期化を実行することです。たとえば、その時点でそのクラスに関連付けられているいくつかの静的変数を初期化したい場合があります。

この最後の部分もよくわかりませんでした。お役に立てれば幸いです。

4

3 に答える 3

3

これは非常に多くの質問が 1 つにまとめられているため、答えるのが非常に困難です。

したがって、私の最初の質問は、「指定された」初期化子である特定の 1 つをすべて使用する場合、他のすべての初期化方法があるのはなぜですか?

主に発信者の便宜のために。たとえば、指定されたイニシャライザがinitWithX:y:width:height:であるとしますが、次のようなものをあちこちに書いていることに気づきます。

[[MyRect alloc] initWithX:0 y:0 width:0 height:0]
[[MyRect alloc] initWithX:myPoint.x y:myPoint.y width:mySize.width height:mySize.height]

別のいくつかの初期化子を追加したい場合があるので、これを行うだけです:

[[MyRect alloc] initWithEmptyRect]
[[MyRect alloc] initWithPoint:myPoint size:mySize]

もちろん、実際に必要というわけではありませんが、何度も繰り返すのではなく、常に何かを関数にラップする価値があるのと同じ理由で、実行する価値があります。

Foundation/Cocoa に付属するさまざまなクラスを調べると、より現実的な例を見つけることができます。それらの多くにはさまざまな初期化子があり、多くの場合、.x、.y、.width、および .身長。たとえば、NSDictionary には次のようなメソッドがあり-initWithContentsOfURL:ます。理論的には、常にその URL のコンテンツを読み取り、plist をオブジェクトとキーの C スタイルの配列のペアに解析してから を呼び出す-initWithObjects:forKeys:count:ことができるため、これは実際には必要ありません。しかし、どちらをしたいですか?

「指定された初期化子を作成すると、メインの初期化コードが単一のメソッドに集中化されます。クラスをサブクラス化する人は誰でも、指定された初期化子をオーバーライドして、新しいインスタンスが適切に初期化されるようにすることができます」.

この例を挙げていただけますか?彼の言っていることが理解できたのかよくわからない.

誰かがこのクラスを作成したとしましょう:

@interface MySuperRect: MyRect
- (id)initWithX:x y:y width:width height:height;
@end

彼はすべての init メソッドをオーバーライドする必要はありません。これだけです。あなたがこれをするとしましょう:

[[MySuperRect alloc] initWithEmptyRect]

MySuperRect は initWithEmptyRect を実装していないため、指定された初期化子を呼び出すだけの MyRect からの実装が使用されます。しかし、MySuperRect指定された初期化子を実装しています。したがって、オーバーライドが呼び出されます。

それはあなたの3番目の質問にも答えていると思います。(3番目の質問だと思います。)

最後の 4 番目の質問では、誰かがあなたを助ける前に、理解できない部分を説明する必要があります。たとえば、クラスが (わずかに) 特別な種類のオブジェクトであり、一般的にオブジェクトの初期化がどのように機能するか、およびクラスの初期化がどのように特別であるかを理解していますが、そこで静的変数を初期化したい理由を理解していませんか?

于 2012-06-14T00:15:26.160 に答える
1

この場合、「指定された」初期化子である特定の1つをすべて使用する場合、他のすべての初期化方法があるのはなぜですか?

便宜上、これを行います。あなたの「指定された」初期化子(Stephen Kochanが言及しているように)は、通常、最も多くの引数を持つものです。他のすべての初期化子は、いくつかのデフォルト設定でそれを呼び出す便利なメソッドです。たとえば、さまざまな要素の数を指定できる「車」クラスがあるとします。

@implementation Car

//Designated initializer - has tons of arguments
- (id)initWithDoors:(int)doors windows:(int)windows wheels:(int)wheels axles:(int)axles
{
    //implementation
}

//most axles have two wheels and most doors have a window (and there's the front and back window)
- (id)initWithDoors:(int)doors wheels:(int)
{
    return [self initWithDoors:doors windows:doors+2 wheels:wheels axles:wheels/2];
}

//most cars have four doors, four wheels, six windows, and two axles
- (id)init
{
    return [self initWithDoors:4 windows:6 wheels:4 axles:2];
}

@end

サブクラスは、指定された初期化子を呼び出すだけです。クーペクラスの例です。(クーペにはドアが 2 つあります。)

@implementation Coupe //extends Car

//our coupe can have a sunroof, which adds a window
- (id)initWithSunroof:(BOOL)sunroof
{
    self = [super initWithDoors:2 windows:(sunroof?4:5) wheels:4 axles:2];
    if (self) {
      //initialization
    }

    return self;
}

//a default coupe has no sunroof
- (id)init
{
    return [self initWithSunroof:NO];
}

@end

-initWithSunroof:メソッドは、サブクラスの指定された初期化子であることに注意してください。

サブクラス化する場合、なぜこれが重要なのですか?

最後に、-init単純なサブクラス作成者がそれを呼び出して、必要がなければクラスをあまり調査しなくてもすべてのデフォルト値を取得できるように実装する必要があります。

この最後の部分もよくわかりませんでした。

クラス初期化子は次のようにフォーマットされます。

+ (void)initialize
{
    //implementation
}

クラスレベルの変数を設定します。これらは、クラスのすべてのインスタンスに影響します。(クラス変数とインスタンス変数の違いは他の場所で十分に説明されており、この回答の範囲を超えています。混乱が続く場合は、別の質問をしてください。)

于 2012-06-14T00:21:41.020 に答える
0

「最も複雑な初期化子」は、ほとんどのパラメーターを持つものです。他のすべての init-method でそのメソッドを使用するようにする 必要があり
ます 。これらの「それほど複雑ではない」メソッドはすべて、他のすべてのパラメーター (おそらく 0 、...) のデフォルト値を使用して「最も複雑な」メソッドを呼び出す必要があります ( スーパークラスの init-method を呼び出すので、安全に上書きして "最も複雑な "init-Method" を使用して、デフォルトの状態を設定するか、例外をスローします - カスタムを呼び出したい場合は、使用する必要があります )。initWithValueA:andB:andC:initWithValueC:initWithValueA:initWithValueB:andC:nil
[super init]initinit[self init]

あなたが言及した最後の部分は、静的初期化子(「静的クラスコンストラクター」)をカバーしています。その宣言は「+」記号で始まります。これは、alloc のようなクラス メソッド (「静的メソッド」) であることを意味します。C# ではこれはstatic MyClass() {...}、Java ではちょうどstatic { ... }

于 2012-06-14T00:11:04.280 に答える