3

viewForRootVcUIViewサブクラスの宣言されたプロパティであるUIViewがありますNewView。初期化するのはNewViewの責任viewForRootVcですが、サブクラスNewViewSubClassがその背景色を設定します。これがすべて完了すると、ルートビューコントローラはそのビューにサブビューとしてRootVc追加します。viewForRootVc

しかし、これは機能しません。viewForRootVc実際には追加されません。

ただし、次の3つのいずれかを実行すると機能します(ARCを使用していることに注意してください)。

  1. viewForRootVcの背景色を設定するのではなく、で設定NewViewSubClassNewViewます。そして、のインスタンスを初期化するのではなく、NewViewSubClass単に。のインスタンスを初期化しますNewView
  2. で初期化するときviewForRootVcNewView、直接割り当てを使用するのではなく、セッター(つまり、self.viewForRootVc)を呼び出します。
  3. viewForRootVcのヘッダーファイルにivarとしてリストしますNewView

なぜこれら3つが必要なのかわかりません。

動作しないコードは次のとおりです:RootVc.m

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    NewView *newView = [[NewViewSubClass alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];

    //logs 0.00000, 0.00000    
    NSLog(@"%f, %f",newView.viewForRootVc.frame.size.width, newView.viewForRootVc.frame.size.height);

    [self.view addSubview:newView.viewForRootVc];

    //logs 0
    NSLog(@"subviews: %i",[self.view.subviews count]);
}

NewView.h

@property (nonatomic, retain) UIView *viewForRootVc;

NewView.m

@synthesize viewForRootVc;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        viewForRootVc = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    }
    return self;
}

NewViewSubClass.m

@synthesize viewForRootVc;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        viewForRootVc.backgroundColor = [UIColor greenColor];
    }
    return self;
}

私が気づいたことの1つは、ivarとして指定する場合、の実装ファイルviewForRootVcを入れる必要がないことです。@synthesize viewForRootVc;NewViewSubClass

私は常に、宣言されたプロパティが次のことを効果的に達成することを理解していました。

  1. 他のオブジェクトがそれらにアクセスできるようにします。
  2. 必要に応じてセッターが呼び出されるようにします。
  3. retainARCを使用していない場合、プロパティ宣言で指定した場合、セッターを呼び出すとプロパティが自動的に保持されます。
  4. ARCでは、宣言されたプロパティに対してivarが自動的に生成されます。

しかし、明らかにこれ以上のものがあります。それがスコープの問題なのか、それとも上記の機能しないコードが2つの異なるバージョンを作成することになったのかはわかりません。1つはのメソッドviewForRootVcで指定された正しいフレームを持ち、もう1つは正しい背景色を持っています。のメソッドで指定されていますが、どちらにも正しいフレームと色の両方がありません。NewViewinitNewViewSubClassinit

誰かが宣言されたプロパティとivarsの意味を明確にし、セッターを呼び出して宣言されたプロパティを設定するか、値を直接割り当てることができることを強く望んでいます。

4

1 に答える 1

3

したがって、ここでの問題は、NewViewSubClass.mの次の行です。

@synthesize viewForRootVc;

コンパイラーがそこで行うことは、サブクラスの新しいゲッター/セッターのペアを実装することです。これは、スーパークラスの同じプロパティに実装された以前のゲッター/セッターのペアとは異なります。

さらに、ここと前の@synthesizeディレクティブで何か別のことが起こります。これは、コンパイラがこのプロパティをバックアップするためのivarも生成することです。ただし、作成されたこのivarは、特定の各実装内でのみ表示されます。つまり、2つの@synthesizeディレクティブのそれぞれが、独自の個別のivarを作成することになります。

それで、ここNewViewSubClass.mのコードのこのビットに到達します:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {

        // this ivar is the one from the subclass. It is still nil at this point!
        viewForRootVc.backgroundColor = [UIColor greenColor];
    }
    return self;
}

viewForRootVcこれがサブクラスのivarです。この時点ではゼロです。実際、あなたの場合、それは常にゼロであり、設定されることはありません。NewView.mからこのコードを見ると、次のようになります。

@synthesize viewForRootVc;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {

        // Here viewForRootVc is the ivar generated for this class
        // this is not the same as the ivar with the same name in our subclass

        viewForRootVc = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    }
    return self;
}

スーパークラスは、サブクラスで生成された同じ名前のivarではなく、「viewForRootVc」と呼ばれるivarを設定しています。

それが理にかなっていることを願っています。それでは、これを「修正」した3つの方法を見て、それぞれの場合に何が起こったのかを正確に理解しましょう。

  1. NewViewで背景を設定->これは基本的に正しいivar(NewViewからのもの)を使用し、これまで話してきた問題を効果的に回避します。

  2. NewViewからセッターを呼び出す->これは興味深いものです。セッターを呼び出すことにより、実際にはサブクラスに実装されているセッターを呼び出しています。これにより、サブクラスでivarが使用されるため、すべてが「機能」します(ただし、これは理想的ではないと思います)。

  3. ivarを明示的に宣言する->これにより、別の方法で問題が回避されます。ivarを宣言することにより、コンパイラが@synthesizeディレクティブから独自のivarを生成するのを防ぎます。また、ivarはデフォルトで@protectedであるため、そのivarはサブクラスにも表示され、セッター/ゲッターの両方のセットが同じivarを使用することになります。

正しい修正であるIMOは、ivarをクラスに対してプライベートにし(@synthesized ivarは非表示であるため、事実上プライベート)、必要に応じてサブクラスがプロパティgetter/setterを参照するようにします。そしてもちろん、NewViewSubclass.mでプロパティを@synthesizeしないでください。

それがお役に立てば幸いです。

于 2012-01-09T19:05:59.080 に答える