Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

keyPathsForValuesAffecting<Key>

タグ: Objective-C
#import <Foundation/Foundation.h>

@interface Foo : NSObject
{
	NSString* left;
	NSString* right;
}
- (void)setLeft:(NSString*)newLeft right:(NSString*)newRight;
@property (nonatomic, retain) NSString* left;
@property (nonatomic, retain) NSString* right;
@property (nonatomic, readonly) NSString* combined;
@end

@implementation Foo
@synthesize left, right;

+ (NSSet*)keyPathsForValuesAffectingCombined
{
	return [NSSet setWithObjects:@"left", @"right", nil];
}

- (void)dealloc
{
	[right release];
	[left release];
	[super dealloc];
}

/* 手動通知の宣言をしなくても willChangeValueForKey は正しく動作しているようだが、さて……
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString*)theKey
{
	if([theKey isEqualToString:@"left"] || [theKey isEqualToString:@"right"])
		return NO;
	
	return [super automaticallyNotifiesObserversForKey:theKey];
}

- (void)setLeft:(NSString*)newLeft
{
	if(left != newLeft)
	{
		[self willChangeValueForKey:@"left"];
		[left release];
		left = [newLeft retain];
		[self didChangeValueForKey:@"left"];
	}
}

- (void)setRight:(NSString*)newRight
{
	if(right != newRight)
	{
		[self willChangeValueForKey:@"right"];
		[right release];
		right = [newRight retain];
		[self didChangeValueForKey:@"right"];
	}
}
*/

- (void)setLeft:(NSString*)newLeft right:(NSString*)newRight
{
	[self willChangeValueForKey:@"left"];
	[self willChangeValueForKey:@"right"];
	if(left != newLeft)
	{
		[left release];
		left = [newLeft retain];
	}
	if(right != newRight)
	{
		[right release];
		right = [newRight retain];
	}
	[self didChangeValueForKey:@"right"];
	[self didChangeValueForKey:@"left"];
}

- (NSString*)combined
{
	return [NSString stringWithFormat:@"%@ %@", left, right];
}

@end

@interface Bar : NSObject
@end

@implementation Bar
- (void)observeValueForKeyPath:(NSString*)keyPath
                      ofObject:(id)object
						change:(NSDictionary*)change
                       context:(void*)context
{
	NSLog(
		@"%@ %@ %@",
		[change objectForKey:NSKeyValueChangeNotificationIsPriorKey],
		keyPath,
		[change objectForKey:NSKeyValueChangeNewKey]
	);
}
@end

int main(int argc, const char* argv[])
{
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
	Foo* foo = [[Foo alloc] init];
	Bar* bar = [[Bar alloc] init];
	[foo
		addObserver: bar
		forKeyPath: @"left"
		options: NSKeyValueObservingOptionNew
		context: NULL
	];
	[foo
		addObserver: bar
		forKeyPath: @"right"
		options: NSKeyValueObservingOptionNew
		context: NULL
	];
	[foo
		addObserver: bar
		forKeyPath: @"combined"
		options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionPrior
		context: NULL
	];
	foo.left = @"(-";
	foo.right = @"-)";
	[foo setLeft:@"(^" right:@"^)"];
	[foo removeObserver:bar forKeyPath:@"combined"];
	[foo removeObserver:bar forKeyPath:@"right"];
	[foo removeObserver:bar forKeyPath:@"left"];
	[bar release];
	[foo release];
	[pool drain];
    return 0;
}

Xcode 3.1.1 で実行。

1 combined (null)
(null) combined (- (null)
(null) left (-
1 combined (null)
(null) combined (- -)
(null) right -)
1 combined (null)
1 combined (null)
(null) combined (^ ^)
(null) right ^)
(null) combined (^ ^)
(null) left (^

ドキュメントの通りに変化の通知をネストさせれば、 combined の変更通知は1回に収まるのではないかと期待したのだが……(^ ^)(^ ^)
このような「依存関係を持つキー」に限らず、「一固まりの変更」を厳密に認識するには、 NSKeyValueObservingOptionPrior を指定して willChangeValueForKeydidChangeValueForKey のネストを自分で正しく把握しなければならないようだ。

スポンサーサイト

コメント

コメントの投稿

コメントの投稿
管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://idlysphere.blog66.fc2.com/tb.php/141-12d1f992
この記事にトラックバックする(FC2ブログユーザー)

Appendix

タグ

Blog内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。