jens@0: // jens@0: // KVUtils.m jens@0: // MYUtilities jens@0: // jens@0: // Created by Jens Alfke on 2/25/08. jens@0: // Copyright 2008 Jens Alfke. All rights reserved. jens@0: // jens@0: jens@0: #import "KVUtils.h" jens@0: jens@0: jens@0: @implementation Observance jens@0: jens@0: - (id) initWithTarget: (id)target jens@0: action: (SEL)action jens@0: observed: (id)observed jens@0: keyPath: (NSString*)keyPath jens@0: options: (NSKeyValueObservingOptions)options jens@0: { jens@0: self = [super init]; jens@0: if (self != nil) { jens@0: _target = target; jens@0: _action = action; jens@0: _observed = observed; jens@0: _keyPath = [keyPath copy]; jens@0: _options = options; jens@0: jens@0: options &= ~(MYKeyValueObservingOptionOnce | MYKeyValueObservingOptionDelayed); jens@0: jens@0: [_observed addObserver: self forKeyPath: _keyPath options: options context: _action]; jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: - (void) dealloc jens@0: { jens@0: [_observed removeObserver: self forKeyPath: _keyPath]; jens@0: [_keyPath release]; jens@0: [super dealloc]; jens@0: } jens@0: jens@0: jens@0: @synthesize observed=_observed, keyPath=_keyPath; jens@0: jens@0: jens@0: - (void) stopObserving jens@0: { jens@0: [_observed removeObserver: self forKeyPath: _keyPath]; jens@0: _observed = nil; jens@0: _target = nil; jens@0: _action = NULL; jens@0: } jens@0: jens@0: jens@0: - (void) _callTargetWithChange: (NSDictionary*)change jens@0: { jens@0: @try{ jens@0: [_target performSelector: _action withObject: _observed withObject: change]; jens@0: }@catch( NSException *x ) { jens@0: Warn(@"Uncaught exception in -[%@<%p> %s] while observing change of key-path %@ in %@<%p>: %@", jens@0: _target,_target, _action, _keyPath, _observed,_observed, x); jens@3: [NSApp reportException: x]; jens@0: } jens@0: } jens@0: jens@0: jens@0: - (void)observeValueForKeyPath:(NSString *)keyPath jens@0: ofObject:(id)object jens@0: change:(NSDictionary *)change jens@0: context:(void *)context jens@0: { jens@0: if( object == _observed ) { jens@0: if( _options & MYKeyValueObservingOptionDelayed ) jens@0: [self performSelector: @selector(_callTargetWithChange:) withObject: change jens@0: afterDelay: 0.0]; jens@0: else jens@0: [self _callTargetWithChange: change]; jens@0: if( _options & MYKeyValueObservingOptionOnce ) jens@0: [self stopObserving]; jens@0: } jens@0: } jens@0: jens@0: jens@0: @end jens@0: jens@0: jens@0: jens@0: jens@0: @implementation Observer jens@0: jens@0: jens@0: - (id) init jens@0: { jens@0: return [self initWithTarget: self]; jens@0: } jens@0: jens@0: jens@0: jens@0: - (id) initWithTarget: (id)target jens@0: { jens@0: Assert(target); jens@0: self = [super init]; jens@0: if (self != nil) { jens@0: _target = target; jens@0: _observances = [[NSMutableArray alloc] init]; jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: jens@0: - (void) dealloc jens@0: { jens@0: [self stopObserving]; jens@0: [_observances release]; jens@0: [super dealloc]; jens@0: } jens@0: jens@0: jens@0: @synthesize target=_target; jens@0: jens@0: jens@0: - (void) observe: (id)observed jens@0: keyPath: (NSString*)keyPath jens@0: options: (NSKeyValueObservingOptions)options jens@0: action: (SEL)action jens@0: { jens@0: Observance *o = [[Observance alloc] initWithTarget: _target jens@0: action: action jens@0: observed: observed jens@0: keyPath: keyPath jens@0: options: options]; jens@0: [_observances addObject: o]; jens@0: [o release]; jens@0: } jens@0: jens@0: jens@0: - (void) observe: (id)observed jens@0: keyPath: (NSString*)keyPath jens@0: action: (SEL)action jens@0: { jens@0: [self observe: observed keyPath: keyPath options: 0 action: action]; jens@0: } jens@0: jens@0: jens@0: - (void) stopObserving jens@0: { jens@0: [_observances makeObjectsPerformSelector: @selector(stopObserving)]; jens@0: [_observances removeAllObjects]; jens@0: } jens@0: jens@0: jens@3: - (void) stopObserving: (id)observed jens@3: { jens@3: [self stopObserving: observed keyPath: nil]; jens@3: } jens@3: jens@3: jens@0: - (void) stopObserving: (id)observed keyPath: (NSString*)keyPath jens@0: { jens@0: // observed or keyPath may be nil jens@0: for( int i=_observances.count-1; i>=0; i-- ) { jens@0: Observance *o = [_observances objectAtIndex: i]; jens@0: if( (observed==nil || observed==o.observed) && (keyPath==nil || [keyPath isEqual: o.keyPath]) ) { jens@0: [o stopObserving]; jens@0: [_observances removeObjectAtIndex: i]; jens@0: } jens@0: } jens@0: } jens@0: jens@0: @end jens@11: jens@11: jens@11: /* jens@11: Copyright (c) 2008, Jens Alfke . All rights reserved. jens@11: jens@11: Redistribution and use in source and binary forms, with or without modification, are permitted jens@11: provided that the following conditions are met: jens@11: jens@11: * Redistributions of source code must retain the above copyright notice, this list of conditions jens@11: and the following disclaimer. jens@11: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions jens@11: and the following disclaimer in the documentation and/or other materials provided with the jens@11: distribution. jens@11: jens@11: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR jens@11: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND jens@11: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- jens@11: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES jens@11: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR jens@11: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN jens@11: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF jens@11: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jens@11: */