3

私はもともと次のように書くつもりでした:

-(void) setLeftChild:(NSNode *) leftChildNode {

     _leftChildNode = leftChildNode;
     leftChildNode.parent = self;
     // ...
}

ノードの子にすべての適切な値を設定し、子ノードの親ノードは自分自身を指す必要があり、子ノードのツリー オブジェクトは親のツリー オブジェクト (ノードが属するツリー) と同じである必要があります。

しかし、そうすると単純な行に影響することに気付きました。そのため、node.leftChild = something; この行には、予期しない「副作用」が発生する可能性があります。

また、setLeftChild実際に他のプロパティのセッターを使用し (leftChildNode.parent = self適切なインターフェイスである を使用するなど)、それらのセッターがノードのleftChildセッターを使用するとどうなりますか? このように無限ループになる可能性があります。特殊なケースでこの無限ループが発生する場合はあまり良くなく、潜在的な無限ループについて常に心配する必要があります。

もう 1 つの方法は、合成されたセッターをオーバーライドせず、 のaddLeftChildNode代わりに名前を使用することです。setLeftChildNode内部addLeftChildNodeでは、プロパティ セッターを心配なく自由に使用できます。しかし、本当に「設定」したい場合はどうでしょうか?「追加」という名前は、空の場合にのみ追加することを意味するので、「設定」という名前がより適切です。addLeftChildの代わりに呼び出すとaddLeftChildNode、これら 2 つの名前が紛らわしくなります。it を呼び出すなどの規則が使用されている場合setAndConfigLeftChildNode、混乱を招くことはなく、明らかにセッターをオーバーライドすることもなく、コードで従う規則になる可能性があります。(setAndConfigセッターをオーバーライドする代わりに使用)

したがって、次の問題があります。

1) オーバーライドされたセッターの予期しない副作用 (質問の冒頭)
2) 無限ループ
3) メソッドの命名、混乱する可能性があります

良い共通/ベストプラクティスはありますか?

4

4 に答える 4

3

良い質問です...それは本当に好みに要約されますが、次のようにセッター関数の名前を変更することもできることを指摘したいと思います:

@property (setter=assignLeftChildNode) NSNode *leftChildNode;

そのため、命名規則に関してもう少し柔軟性があります...setLeftChildNode合成されたセッターが採用する必要はありません。これがあなたの質問に対する直接的な回答ではないことは承知していますが、これがお役に立てば幸いです。

于 2012-09-08T04:12:44.537 に答える
1

命名規則(他の質問ですでに説明されています)とは別に、(すでにお気づきのように)副作用を簡単に監視できないため、セッター関数をオーバーライドしません。私は別の方法を使用しますsetLeftTree:

また、ノードにすでに左の子があり、新しい子を設定する場合は、前の子の親を次のように設定することも検討する必要がありますnil

- (void)setLeftTree:(NSNode *)leftChildNode
{
    if (self.leftChild != nil)
        self.leftChild.parent = nil;
    self.leftChild = leftChildNode;
    leftChildNode.parent = self;
}
于 2012-09-08T06:27:45.437 に答える
1

これについて少し考えてみると、答えは、関係を一方から完全に制御する必要があるということだと思います。parent子ノードのプロパティがweak保持サイクルを停止する可能性があることを考えると、親を関係の所有者として指定します。これの最も簡単な表現は、親を設定するメソッドがクラスに対してプライベートであるということです ( NB はクラスを呼び出さないでくださいNSNode。NS プレフィックスは Apple によって予約されています)。ドット表記やドット表記に取り組みsetParentたくないので、呼び出すべきではありません。-setValue:forKey:このメソッドは、親 ivar を設定するだけです。参照整合性を維持するためのすべてのロジックは、setLeftChild:(および推定可能setRightChild:な) メソッド内にある必要があります。実装は次のようになります。

// Assuming you are using ARC
@interface MyNode()

-(void) privateSetParent: (MyNode*) newParent; 

@end

@implementation MyNode
{
    _weak MyNode* _parent;
    MyNode* _leftTree;
    MyNode* _rightTree;
}

-(void) privateSetParent: (MyNode*) newParent
{
    _parent = newParent;
}

-(void) setLeftTree: (MyNode*) newTree
{
    [[newTree parent] remove: newTree];
    [_leftTree privateSetParent: nil];
    _leftTree = newTree;
    [newTree privateSetParent: self];
}

-(void) remove: (MyNode*) subTree
{
    if (_leftTree == subTree)
    {
        _leftTree = nil;
        [subTree privateSetParent: nil];
    }
    if (_rightTree == subTree)
    {
        _rightTree = nil;
        [subTree privateSetParent: nil];
    }   
} 

@end

読み取り専用のプロパティを引き続き使用でき、手動変更通知parentで KVO を実行できます。

于 2012-09-08T09:50:43.917 に答える