2011-06-23 12 views
2

データツリーのノードを実装するObjective-Cクラスがあります。クラスのプライベートエクステンション(ここでは示されていません)は、プロパティーのセッターを実装しているので、マネージャークラスはツリーを作成できます。Objective-Cカスタムプロパティゲッター無限再帰

// Interface 
@interface DataSet : NSObject { 
    NSString  *name; 
    NSString  *data; 
@private 
    DataSet   *parent; 
    NSMutableArray *children; 
} 
@property (nonatomic, readonly, copy) NSString *name; 
@property (nonatomic, readonly, copy) NSString *data; 

は、私はそれがそのプロパティの非nilの値を持つ祖先ノードを見つけるまでツリーを歩いていく、プロパティがnilの場合、プロパティのいずれかのカスタムゲッターを実装したいです。

私の問題は、それ自体を呼び出すゲッターの無限再帰を引き起こさずにゲッターを実装することです。

// Implementation 
@interface DataSet() 
@property (nonatomic, retain) DataSet   *parent; 
@property (nonatomic, retain) NSMutableArray *children; 
@end 

@implementation DataSet 

@synthesize name; 
// do not @synthesize data 
@synthesize parent, children; 

// custom getter walks up tree to find first non-nil 'data' property 
- (NSString*) data { 
    NSString *result = nil; 
    DataSet *set = self; 
    while (set != nil && result == nil) { 
     result = [set data]; // <=== INFINITE RECURSION HERE 
     set = set.parent; 
    } 
    return result; 
} 

私はこのフォーラムや他のフォーラムで検索しましたが、ここで何をしようとしているのかの例は見つかりませんでした。誰でも何か提案がありますか?

また、ゲッターの最後の行は

​​

すべきですか?

答えて

5

うーん、私はあなたがこのような何かをしたいと思う:

-(NSString *) data { 
    // Determine result from current instance data. 
    NSString *result = ....; 

    // If nothing, ask parent instance of this instance. 
    if (result == nil) { 
     result = [parent data]; 
    } 

    // Might still be nil if parent returns nothing. 
    return result; 
} 

うーん、実際にあなたには、いくつかのテクスチャデータを含むdata変数を持っているとして、それは次のように行うことができる見る:

-(NSString *) data { 
    // If data is nil, ask parent instance for a value, otherwise return a copy. 
    return data == nil ? [parent data] : [data copy]; 
} 

したがって、DataSetの各インスタンスはループを持つ必要はありません。彼らがするのは、彼らの直系の親とチェックすることだけです。このようにして、A→B→C→Dのデータグラフがあり、[D data];を実行すると、Dはそれ自身をチェックし、次にCとチェックし、次にBと判断して自分自身をチェックしてからAと尋ねます。結果の最初の正常なnil以外の値が返されます。

+0

これはうまくいくでしょうが、Objective-Cメッセージを送るwhileループの1つのメッセージは、通常のC関数呼び出しよりも少しオーバーヘッドがあります(ダイナミックディスパッチとは何か?)。 –

+0

私はJavaからObjective Cへと移行しています。そのため、ルーピングの方が速いかもしれませんが、私はObjective Cを個人的に見ています。もちろん、多くの呼び出しに起因する実際の速度の問題がありました。 :-) – drekka

+0

このアプローチのもう1つの利点は、ツリー内に 'DataSet'のサブクラスを含めることをサポートしていることです。サブクラスは' -data'ゲッターをオーバーライドする可能性がありますが、iVarに直接アクセスすると無効になります。このソリューションは、より短く、より明確で、より多くの "Objective-"です。他のオブジェクト 'iVars'モデルで "while"ループを回避することは避けたいと思います。 –

3

だけで直接IVARにアクセス:

// custom getter walks up tree to find first non-nil 'data' property 
- (NSString*) data { 
    NSString *result = nil; 
    DataSet *set = self; 
    while (set != nil && result == nil) { 
     result = set->data; 
     set = set->parent; 
    } 
    return [result copy]; 
} 

をこれは、プロパティアクセサを呼び出し、したがって、再帰を回避回避します。

そして最後の行は、copyプロパティを持つプロパティとして宣言しているため、return [result copy];である必要があります。 copyプロパティで宣言していない場合は、コピーを返さないでしょう。

+0

ivarsに直接アクセスしないでください。カプセル化が中断され、サブクラス化が困難になります(サブクラスは動作をオーバーライドできなくなります)。 – bbum

+0

ありがとう、アダム!私はそのプロパティの値に直接アクセスする方法がなければならないことを知っていましたが、それを機能させるための適切な組み合わせを見つけることはできませんでした。これは仕事です! – SteveCaine

+0

いいえ、実際はそうではありません。カプセル化を破っています。根本的な問題は、あなたのアルゴリズムが状況に間違っていたことです。デレクのソリューション(またはそのようなもの)を使用してください。オブジェクトの内容にアクセスするために ' - > 'を使うことは**言語の基本的なパターン**に完全に反する。 – bbum

0

解決しました。

オブジェクトの(ただし、オブジェクト自体ではありません)でカスタムゲッターを再帰的に呼び出すことができます。それは問題ではありません。

キーは、オブジェクトを解放するときにdeallocでカスタムゲッターを使用しないことです。そうでなければ、いくつかの 'データ'オブジェクトは複数回返されます。

// custom getter - if data is nil on this object, 
// find the first non-nil value in its list of ancestors 
- (NSString*) data { 
    NSString *result = data; 
    if (result == nil && parent != nil) 
     result = [parent data]; 
    return result; 
} 

- (void) dealloc { 
    NSInteger count = [children count]; 
    for (NSInteger index = count - 1; index >= 0; --index) { 
     DataSet *child = [children objectAtIndex:index]; 
     [children removeLastObject]; 
     [child release]; 
    } 
    [children release]; 

    // DON'T call custom getter if data is nil 
    // or we'll get one of its ancestors' data object, release it, 
    // then later release it again when releasing the ancestor 
    if (data != nil) 
     [self.data release]; 

    [self.name release]; 

    [super dealloc]; 
} 
+0

このコードに関するいくつかのこと。 1. NSArrayに保持されているオブジェクトを解放するコードを記述する必要はありません。それは自動的にそれを行います。そうすることでループに乗ることができます。 2. Objective Cはnilとsetterにメッセージを送ることができるので、古い値のリリースを扱うことができるので、 'self() .data = nil; '長い話を短くするには:-)あなたはできます:' - (void)dealloc {self.children = nil; self.name = nil; self.data = nil; [super dealloc]; } ' – drekka

+0

ああ、光が鳴ります。私はC++のバックグラウンドから来ているので、私はメモリ管理について強く思う傾向があります。あなたは、私のコードが、私のために何をするのかをするのに苦労していると言っています。インストゥルメントを実行し、いくつかのalloc/releaseの問題を修正した後、あなたのコードを使用すると、すべてのオブジェクトが解放されています。ありがとう! – SteveCaine

+0

上記のコードには何も問題はありません。多くの場合、SDKにコードが組み込まれています。したがって、通常は保存/解放がいつ行われるかを教えてくれるので、常にdocoをチェックする価値があります。 – drekka

関連する問題