PageRenderTime 54ms CodeModel.GetById 1ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 0ms

/BlocksKit/NSObject+BlockObservation.m

http://github.com/zwaldowski/BlocksKit
Objective C | 147 lines | 106 code | 37 blank | 4 comment | 7 complexity | a3de8d1c03c2037c5d596309b0d8571a MD5 | raw file
  1//
  2//  NSObject+BlockObservation.m
  3//  BlocksKit
  4//
  5
  6#import "NSObject+BlockObservation.h"
  7#import "NSObject+AssociatedObjects.h"
  8#import "NSDictionary+BlocksKit.h"
  9
 10@interface BKObserver : NSObject
 11
 12@property (nonatomic, assign) id observee;
 13@property (nonatomic, copy) NSString *keyPath;
 14@property (nonatomic, copy) BKObservationBlock task;
 15
 16+ (BKObserver *)observerForObject:(id)observee keyPath:(NSString *)keyPath task:(BKObservationBlock)task;
 17
 18@end
 19
 20static char kObserverBlocksKey;
 21static char kBlockObservationContext;
 22
 23@implementation BKObserver
 24
 25@synthesize observee, keyPath, task;
 26
 27+ (BKObserver *)observerForObject:(id)observee keyPath:(NSString *)newKeyPath task:(BKObservationBlock)newTask {
 28	BKObserver *instance = [BKObserver new];
 29	instance.observee = observee;
 30	instance.keyPath = newKeyPath;
 31	instance.task = newTask;
 32	return [instance autorelease];
 33}
 34
 35- (void)observeValueForKeyPath:(NSString *)aKeyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
 36	if (context == &kBlockObservationContext)
 37		self.task(object, change);
 38}
 39
 40- (void)dealloc {
 41	self.task = nil;
 42	self.keyPath = nil;
 43	[super dealloc];
 44}
 45
 46@end
 47
 48
 49static dispatch_queue_t BKObserverMutationQueue() {
 50	static dispatch_queue_t queue = nil;
 51	static dispatch_once_t token = 0;
 52	dispatch_once(&token, ^{
 53		queue = dispatch_queue_create("org.blockskit.observers.queue", 0);
 54	});
 55	return queue;
 56}
 57
 58@implementation NSObject (BlockObservation)
 59
 60- (NSString *)addObserverForKeyPath:(NSString *)keyPath task:(BKObservationBlock)task {
 61	NSString *token = [[NSProcessInfo processInfo] globallyUniqueString];
 62	[self addObserverForKeyPath:keyPath identifier:token task:task];
 63	return token;
 64}
 65
 66- (void)addObserverForKeyPath:(NSString *)keyPath identifier:(NSString *)identifier task:(BKObservationBlock)task {
 67	NSParameterAssert(keyPath);
 68	NSParameterAssert(identifier);
 69	NSParameterAssert(task);
 70	
 71	__block BKObserver *newObserver = nil;
 72	
 73	dispatch_sync(BKObserverMutationQueue(), ^{
 74		newObserver = [BKObserver observerForObject:self keyPath:keyPath task:task];
 75		
 76		NSMutableDictionary *dict = [self associatedValueForKey:&kObserverBlocksKey];
 77		if (!dict) {
 78			dict = [NSMutableDictionary dictionary];
 79			[self associateValue:dict withKey:&kObserverBlocksKey];
 80		}
 81		
 82		[dict setObject:newObserver forKey:[NSString stringWithFormat:@"%@_%@", keyPath, identifier]];
 83	});
 84	
 85	[self addObserver:newObserver forKeyPath:keyPath options:0 context:&kBlockObservationContext];
 86}
 87
 88- (NSString *)addObserverForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options task:(BKObservationBlock)task {
 89	NSString *token = [[NSProcessInfo processInfo] globallyUniqueString];
 90	[self addObserverForKeyPath:keyPath identifier:token options:options task:task];
 91	return token;
 92}
 93
 94- (void)addObserverForKeyPath:(NSString *)keyPath identifier:(NSString *)identifier options:(NSKeyValueObservingOptions)options task:(BKObservationBlock)task {
 95	NSParameterAssert(keyPath);
 96	NSParameterAssert(identifier);
 97	NSParameterAssert(task);
 98	
 99	__block BKObserver *newObserver = nil;
100	
101	dispatch_sync(BKObserverMutationQueue(), ^{
102		newObserver = [BKObserver observerForObject:self keyPath:keyPath task:task];
103		
104		NSMutableDictionary *dict = [self associatedValueForKey:&kObserverBlocksKey];
105		if (!dict) {
106			dict = [NSMutableDictionary dictionary];
107			[self associateValue:dict withKey:&kObserverBlocksKey];
108		}
109		
110		[dict setObject:newObserver forKey:[NSString stringWithFormat:@"%@_%@", keyPath, identifier]];
111	});
112	
113	[self addObserver:newObserver forKeyPath:keyPath options:options context:&kBlockObservationContext];
114}
115
116- (void)removeObserverForKeyPath:(NSString *)keyPath identifier:(NSString *)identifier {
117	NSParameterAssert(keyPath);
118	NSParameterAssert(identifier);
119	
120	dispatch_sync(BKObserverMutationQueue(), ^{
121		NSString *token = [NSString stringWithFormat:@"%@_%@", keyPath, identifier];
122		NSMutableDictionary *dict = [self associatedValueForKey:&kObserverBlocksKey];
123		BKObserver *trampoline = [dict objectForKey:token];
124		
125		if (!trampoline || ![trampoline.keyPath isEqualToString:keyPath])
126			return;
127		
128		[self removeObserver:trampoline forKeyPath:keyPath];
129		
130		[dict removeObjectForKey:token];
131		
132		if (!dict.count)
133			[self associateValue:nil withKey:&kObserverBlocksKey];
134	});
135}
136
137- (void)removeAllBlockObservers {
138	dispatch_sync(BKObserverMutationQueue(), ^{
139		NSMutableDictionary *observationDictionary = [self associatedValueForKey:&kObserverBlocksKey];
140		[observationDictionary each:^(id key, id trampoline) {
141			[self removeObserver:trampoline forKeyPath:[trampoline keyPath]];
142		}];
143		[self associateValue:nil withKey:&kObserverBlocksKey];
144	});
145}
146
147@end