Created
January 6, 2010 08:32
-
-
Save nolanw/270137 to your computer and use it in GitHub Desktop.
Slight alteration of Any Matuschak's BlockObservation for MacRuby.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// NSObject+ProcObservation.h | |
// Version 1.0 | |
// | |
// Andy Matuschak | |
// [email protected] | |
// Public domain because I love you. Let me know how you use it. | |
// | |
// NTW 2009-Oct-21: Added selectors with an options argument. | |
// NTW 2009-Oct-30: Transplanted new observation key from MYUtilities's KVUtils. | |
// NTW 2010-Jan-06: Changed to accept Ruby proc instead of C block. Got rid of block token typedef so MacRuby isn't confused. See NSObject_ProcObservation.rb for handy Ruby interface. | |
#import <Cocoa/Cocoa.h> | |
// Or MYKeyValueObservingOptionOnce with your observation options to cause the | |
// observer to remove itself upon the first change notification. | |
// Idea and line of code from MYUtilities. | |
enum { | |
MYKeyValueObservingOptionOnce = 1<<31 | |
}; | |
@interface NSObject (AMRubyBlockObservation) | |
- (NSString *)addObserverForKeyPath:(NSString *)keyPath proc:(id)proc; | |
- (NSString*)addObserverForKeyPath:(NSString*)keyPath | |
options:(NSKeyValueObservingOptions)options | |
proc:(id)proc; | |
- (NSString*)addObserverForKeyPath:(NSString*)keyPath | |
onQueue:(NSOperationQueue*)queue | |
proc:(id)proc; | |
- (NSString*)addObserverForKeyPath:(NSString*)keyPath | |
options:(NSKeyValueObservingOptions)options | |
onQueue:(NSOperationQueue*)queue | |
proc:(id)proc; | |
- (void)removeObserverWithProcToken:(NSString *)token; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// NSObject+ProcObservation.m | |
// Version 1.0 | |
// | |
// Andy Matuschak | |
// [email protected] | |
// Public domain because I love you. Let me know how you use it. | |
// | |
#import "NSObject+ProcObservation.h" | |
#import <dispatch/dispatch.h> | |
#import <objc/runtime.h> | |
#import <macruby/macruby.h> | |
@interface AMRubyObserverTrampoline : NSObject | |
{ | |
__weak id observee; | |
NSString *keyPath; | |
id proc; | |
NSOperationQueue *queue; | |
NSKeyValueObservingOptions options; | |
} | |
- (AMRubyObserverTrampoline*)initObservingObject:(id)obj | |
keyPath:(NSString*)newKeyPath | |
options:(NSKeyValueObservingOptions)newOptions | |
onQueue:(NSOperationQueue*)newQueue | |
proc:(id)newProc; | |
- (void)cancelObservation; | |
@end | |
@implementation AMRubyObserverTrampoline | |
static NSString *AMRubyObserverTrampolineContext = @"AMRubyObserverTrampolineContext"; | |
- (AMRubyObserverTrampoline*)initObservingObject:(id)obj | |
keyPath:(NSString*)newKeyPath | |
options:(NSKeyValueObservingOptions)newOptions | |
onQueue:(NSOperationQueue*)newQueue | |
proc:(id)newProc | |
{ | |
self = [super init]; | |
if (self != nil) | |
{ | |
proc = newProc; | |
keyPath = [newKeyPath copy]; | |
queue = [newQueue retain]; | |
observee = obj; | |
options = newOptions; | |
// Clear out our customized options before passing them on. | |
// From MYUtilities. | |
newOptions &= ~MYKeyValueObservingOptionOnce; | |
[observee addObserver:self | |
forKeyPath:keyPath | |
options:newOptions | |
context:AMRubyObserverTrampolineContext]; | |
} | |
return self; | |
} | |
- (void)observeValueForKeyPath:(NSString*)aKeyPath | |
ofObject:(id)object | |
change:(NSDictionary*)change | |
context:(void*)context | |
{ | |
if (context != AMRubyObserverTrampolineContext) | |
{ | |
[super observeValueForKeyPath:aKeyPath ofObject:object change:change context:context]; | |
return; | |
} | |
// Not sure if we really need this on the next run loop iteration. Seemed right. | |
if (options & MYKeyValueObservingOptionOnce) | |
[self performSelector:@selector(cancelObservation) withObject:nil afterDelay:0.0]; | |
if (queue) | |
[queue addOperationWithBlock:^{ [proc performRubySelector:@selector(call:) withArguments:object, change]; }]; | |
else | |
[proc performRubySelector:@selector(call:) withArguments:object, change]; | |
} | |
- (void)cancelObservation | |
{ | |
[observee removeObserver:self forKeyPath:keyPath]; | |
} | |
- (void)dealloc | |
{ | |
[self cancelObservation]; | |
[proc release]; | |
[keyPath release]; | |
[queue release]; | |
[super dealloc]; | |
} | |
@end | |
static NSString *AMRubyObserverMapKey = @"org.andymatuschak.observerMapRuby"; | |
@implementation NSObject (AMRubyBlockObservation) | |
- (NSString *)addObserverForKeyPath:(NSString *)keyPath proc:(id)proc | |
{ | |
return [self addObserverForKeyPath:keyPath options:0 onQueue:nil proc:proc]; | |
} | |
- (NSString*)addObserverForKeyPath:(NSString*)keyPath | |
options:(NSKeyValueObservingOptions)options | |
proc:(id)proc | |
{ | |
return [self addObserverForKeyPath:keyPath options:options onQueue:nil proc:proc]; | |
} | |
- (NSString*)addObserverForKeyPath:(NSString*)keyPath | |
onQueue:(NSOperationQueue*)queue | |
proc:(id)proc | |
{ | |
return [self addObserverForKeyPath:keyPath options:0 onQueue:queue proc:proc]; | |
} | |
- (NSString*)addObserverForKeyPath:(NSString*)keyPath | |
options:(NSKeyValueObservingOptions)options | |
onQueue:(NSOperationQueue*)queue | |
proc:(id)proc | |
{ | |
NSString *token = [[NSProcessInfo processInfo] globallyUniqueString]; | |
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
if (!objc_getAssociatedObject(self, AMRubyObserverMapKey)) | |
objc_setAssociatedObject(self, AMRubyObserverMapKey, [NSMutableDictionary dictionary], OBJC_ASSOCIATION_RETAIN); | |
AMRubyObserverTrampoline *trampoline = [[[AMRubyObserverTrampoline alloc] initObservingObject:self keyPath:keyPath options:options onQueue:queue proc:proc] autorelease]; | |
[objc_getAssociatedObject(self, AMRubyObserverMapKey) setObject:trampoline forKey:token]; | |
}); | |
return token; | |
} | |
- (void)removeObserverWithProcToken:(NSString *)token | |
{ | |
NSMutableDictionary *observationDictionary = objc_getAssociatedObject(self, AMRubyObserverMapKey); | |
AMRubyObserverTrampoline *trampoline = [observationDictionary objectForKey:token]; | |
if (!trampoline) | |
{ | |
NSLog(@"Tried to remove non-existent observer on %@ for token %@", self, token); | |
return; | |
} | |
[trampoline cancelObservation]; | |
[observationDictionary removeObjectForKey:token]; | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# NSObject+ProcObservation.rb | |
# Public Domain | |
# | |
# Created by Nolan Waite on 10-01-06. | |
# Copyright 2010 Nolan Waite. All rights reserved. | |
# Replicated from NSObject+ProcObservation.h | |
MYKeyValueObservingOptionsOnce = 1<<31 | |
class NSObject | |
def observe(keyPath, options=0, &block) | |
self.addObserverForKeyPath(keyPath, options:options, proc:block) | |
end | |
def stopObserving(token) | |
self.removeObserverWithProcToken token | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment