8

これは小さな詳細ですが、何かを遅延ロードするたびに、それに巻き込まれます。これらの方法は両方とも受け入れられますか?どちらが良いですか?変数にretainプロパティがあると仮定します。

方法#1

(AnObject *)theObject{
    if (theObject == nil){
        theObject = [[AnObject createAnAutoreleasedObject] retain];
    }
    return theObject;
}

方法#2

(AnObject *)theObject{
    if (theObject == nil){
        self.theObject = [AnObject createAnAutoreleasedObject];
    }
    return theObject;
}

まず、アクセサー内の別のアクセサー機能にアクセスしてもよいかどうかわかりません(ただし、理由はわかりません)。しかし、セッターが何か特別なことをした場合(または、プロパティが保持以外のものに変更され、ゲッターがチェックされていない場合)、セッターを経由せずにクラス変数を設定することも同様に悪い可能性があるようです。

4

4 に答える 4

17

クラスのクライアントが何をしているかにもよりますが、どちらも実際には非常に壊れやすく、まったく同じではありません。それらを同一にすることは十分に簡単です-以下を参照してください-しかし、それをより脆弱にすることはより困難です。これがレイジー初期化の代償です(そして、なぜ私は一般的にレイジー初期化をこのように避けようとし、サブシステムの初期化を全体的なアプリケーション状態管理の一部として扱うことを好みます)。

#1では、セッターを回避しているため、変更を監視しているものは変更を確認できません。「監視」とは、特にKey-Value監視(KVOを使用してUIを自動的に更新するCocoa Bindingsを含む)を指します。

#2では、変更通知をトリガーし、UIを更新します。それ以外の場合は、セッターが呼び出されたかのようになります。

どちらの場合も、オブジェクトの初期化によってgetterが呼び出されると、無限再帰が発生する可能性があります。これには、オブザーバーが変更通知の一部として古い値を要求した場合も含まれます。そうしないでください。

いずれかの方法を使用する場合は、結果を慎重に検討してください。1つは、プロパティの状態変化が通知されなかったためにアプリを一貫性のない状態のままにする可能性があり、もう1つはデッドロックの可能性があります。

問題を完全に回避することをお勧めします。下記参照。


(ガベージコレクションオン、標準のCocoaコマンドラインツール:

#import <Foundation/Foundation.h>

@interface Foo : NSObject
{
    NSString *bar;
}
@property(nonatomic, retain) NSString *bar;
@end
@implementation Foo
- (NSString *) bar
{
    if (!bar) {
        NSLog(@"[%@ %@] lazy setting", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
        [self willChangeValueForKey: @"bar"];
        bar = @"lazy value";
        [self didChangeValueForKey: @"bar"];
    }
    return bar;
}

- (void) setBar: (NSString *) aString
{
    NSLog(@"[%@ %@] setting value %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aString);
    bar = aString;
}
@end

@interface Bar:NSObject
@end
@implementation Bar
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
{
    NSLog(@"[%@ %@] %@ changed\n\tchange:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), keyPath, change);
}
@end

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Foo *foo = [Foo new];
    Bar *observer = [Bar new];
    CFRetain(observer);
    [foo addObserver:observer forKeyPath:@"bar"
             options: NSKeyValueObservingOptionPrior | NSKeyValueObservingOptionNew
             context:NULL];
    foo.bar;
    foo.bar = @"baz";
    CFRelease(observer);

    [pool drain];
    return 0;
}

これはハングしません。それは吐き出します:

2010-09-15 12:29:18.377 foobar[27795:903] [Foo bar] lazy setting
2010-09-15 12:29:18.396 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    notificationIsPrior = 1;
}
2010-09-15 12:29:18.397 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    new = "lazy value";
}
2010-09-15 12:29:18.400 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    notificationIsPrior = 1;
}
2010-09-15 12:29:18.400 foobar[27795:903] [Foo setBar:] setting value baz
2010-09-15 12:29:18.401 foobar[27795:903] [Bar observeValueForKeyPath:ofObject:change:context:] bar changed
    change:{
    kind = 1;
    new = baz;
}

観察のオプションのリストに追加NSKeyValueObservingOptionOldすると、非常にハングします。

以前に行ったコメントに戻ります。最善の解決策は、ゲッター/セッターの一部として遅延初期化を行わないことです。きめが細かすぎます。オブジェクトグラフの状態をより高いレベルで管理する方がはるかに優れており、その一環として、基本的に「Yo!このサブシステムを使用します!その悪い子をウォームアップします!」という状態遷移があります。 「」それは怠惰な初期化を行います。

于 2010-09-14T19:50:58.930 に答える
3

これらの方法は決して同じではありません。最初のものは正しいですが、2番目のものは間違っています!ゲッターは決して電話will/didChangeValueForKey:をかけないかもしれず、したがってセッターも呼ばないかもしれません。そのプロパティが観察された場合、これは無限の再帰につながります。

また、メンバーの初期化時に監視する状態変化はありません。あなたはあなたのオブジェクトにを求め、あなたtheObjectはそれを手に入れます。これが作成されるときは、実装の詳細であり、外の世界には関係ありません。

于 2010-09-15T13:01:31.623 に答える
1

プロパティセッターメソッドが標準の保持セッターであることがわかっている場合、それらは同じです。そうでない場合は、その操作中にセッターの他の動作を呼び出すかどうかを決定する必要があります。わからない場合は、セッターの動作が重要になる可能性があるため、セッターを使用するのが最も安全です。汗をかかないでください。

于 2010-09-14T19:48:05.963 に答える
0

どちらも基本的に同じです。どちらが自分のケースに最適かを選択するのは、実際にはあなた次第です。プロパティ構文の使用に関する長所/短所については、すでに説明しました。

于 2010-09-14T19:44:16.373 に答える