Skip to content

Instantly share code, notes, and snippets.

@rydermackay
Created December 10, 2013 21:39
Show Gist options
  • Save rydermackay/7900706 to your computer and use it in GitHub Desktop.
Save rydermackay/7900706 to your computer and use it in GitHub Desktop.
KVO in a nut. Use it, or don’t?
//
// RGMKeyValueObservance.h
// RGMKeyValueObservance
//
// Created by Ryder Mackay on 12/9/2013.
// Copyright (c) 2013 Ryder Mackay. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface RGMKeyValueObservance : NSObject
+ (instancetype)observeObject:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options target:(__weak id)target action:(SEL)action;
+ (instancetype)observeObject:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(void (^)(NSDictionary *change))block;
@property (nonatomic, strong, readonly) id object;
@property (nonatomic, copy, readonly) NSString *keyPath;
@end
//
// RGMKeyValueObservance.m
// RGMKeyValueObservance
//
// Created by Ryder Mackay on 12/9/2013.
// Copyright (c) 2013 Ryder Mackay. All rights reserved.
//
#import "RGMKeyValueObservance.h"
@interface RGMKeyValueObservance ()
@property (nonatomic, copy) void (^block)(NSDictionary *change);
@property (nonatomic, assign) NSKeyValueObservingOptions options;
@end
@implementation RGMKeyValueObservance
static NSString * RGMKeyValueObservanceContext = @"RGMObservanceContext";
+ (instancetype)observeObject:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options target:(__weak id)target action:(SEL)action
{
return [self observeObject:object keyPath:keyPath options:options block:^(NSDictionary *change) {
if (target) {
__strong id strongTarget = target;
NSMethodSignature *methodSignature = [strongTarget methodSignatureForSelector:action];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
invocation.selector = action;
[invocation setArgument:&change atIndex:2];
[invocation invokeWithTarget:strongTarget];
} else {
RGMObservanceDeallocatedTargetBreak();
}
}];
}
+ (instancetype)observeObject:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(void (^)(NSDictionary *))block
{
return [[self alloc] initWithObject:object keyPath:keyPath options:options block:block];
}
static inline void RGMObservanceDeallocatedTargetBreak() {
#ifdef DEBUG
NSLog(@"RGMObservance: target of was deallocated while observing--doing nothing. Set a breakpoint in RGMObservanceDeallocatedTargetBreak for more info.");
#endif
}
- (instancetype)initWithObject:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(void (^)(NSDictionary *))block
{
NSParameterAssert(object);
NSParameterAssert(keyPath);
NSParameterAssert(block);
if (self = [super init]) {
_object = object;
_keyPath = [keyPath copy];
_options = options;
_block = [block copy];
[_object addObserver:self forKeyPath:_keyPath options:_options context:&RGMKeyValueObservanceContext];
}
return self;
}
- (void)dealloc
{
[_object removeObserver:self forKeyPath:_keyPath context:&RGMKeyValueObservanceContext];
NSLog(@"-[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p; object = %@; keyPath = %@; options = %zu;>", NSStringFromClass([self class]), self, self.object, self.keyPath, self.options];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == &RGMKeyValueObservanceContext) {
self.block(change);
}
}
@end
@auibrian
Copy link

Considered harmful.

@rydermackay
Copy link
Author

You cheeky bastard!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment