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