Skip to content

Commit

Permalink
Added NSObject+JKSObserving for simple predictable block based KVO
Browse files Browse the repository at this point in the history
  • Loading branch information
js committed Oct 12, 2012
1 parent 57cbcf0 commit 9f1286f
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 0 deletions.
8 changes: 8 additions & 0 deletions JKSToolkit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
0434A9BE1622BD3C0068E5AF /* NSBezierPath+JKSAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0434A9BC1622BD3C0068E5AF /* NSBezierPath+JKSAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
0434A9BF1622BD3C0068E5AF /* NSBezierPath+JKSAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 0434A9BD1622BD3C0068E5AF /* NSBezierPath+JKSAdditions.m */; };
043884B01562984100316949 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04FA9B98154F3B2B00518173 /* Cocoa.framework */; };
043B8C721627FF3300666540 /* NSObject+JKSObserving.h in Headers */ = {isa = PBXBuildFile; fileRef = 043B8C701627FF3300666540 /* NSObject+JKSObserving.h */; settings = {ATTRIBUTES = (Public, ); }; };
043B8C731627FF3300666540 /* NSObject+JKSObserving.m in Sources */ = {isa = PBXBuildFile; fileRef = 043B8C711627FF3300666540 /* NSObject+JKSObserving.m */; };
046B00FE156E727E008743D4 /* NSView+JKSAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 046B00FC156E727E008743D4 /* NSView+JKSAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
046B00FF156E727E008743D4 /* NSView+JKSAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 046B00FD156E727E008743D4 /* NSView+JKSAdditions.m */; };
046B0103156E7B37008743D4 /* JKSFlippedView.h in Headers */ = {isa = PBXBuildFile; fileRef = 046B0101156E7B37008743D4 /* JKSFlippedView.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -71,6 +73,8 @@
043884CA1562984100316949 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
043884CC1562984100316949 /* JKSToolKitTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JKSToolKitTests.h; sourceTree = "<group>"; };
043884CD1562984100316949 /* JKSToolKitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JKSToolKitTests.m; sourceTree = "<group>"; };
043B8C701627FF3300666540 /* NSObject+JKSObserving.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+JKSObserving.h"; path = "Categories/NSObject+JKSObserving.h"; sourceTree = "<group>"; };
043B8C711627FF3300666540 /* NSObject+JKSObserving.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+JKSObserving.m"; path = "Categories/NSObject+JKSObserving.m"; sourceTree = "<group>"; };
046B00FC156E727E008743D4 /* NSView+JKSAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSView+JKSAdditions.h"; path = "Categories/NSView+JKSAdditions.h"; sourceTree = "<group>"; };
046B00FD156E727E008743D4 /* NSView+JKSAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSView+JKSAdditions.m"; path = "Categories/NSView+JKSAdditions.m"; sourceTree = "<group>"; };
046B0101156E7B37008743D4 /* JKSFlippedView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JKSFlippedView.h; path = Classes/JKSFlippedView.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -259,6 +263,8 @@
04FA9BC5154F3B5700518173 /* Categories */ = {
isa = PBXGroup;
children = (
043B8C701627FF3300666540 /* NSObject+JKSObserving.h */,
043B8C711627FF3300666540 /* NSObject+JKSObserving.m */,
04FA9BCF154F3E0400518173 /* NSArray+JKSToolkit.h */,
04FA9BD0154F3E0400518173 /* NSArray+JKSToolkit.m */,
04200B5F1552962D002CF16D /* NSString+JKSAdditions.h */,
Expand Down Expand Up @@ -306,6 +312,7 @@
046B00FE156E727E008743D4 /* NSView+JKSAdditions.h in Headers */,
0490B39115B6AA6400EDEAB8 /* CATransaction+JKSAdditions.h in Headers */,
0434A9BE1622BD3C0068E5AF /* NSBezierPath+JKSAdditions.h in Headers */,
043B8C721627FF3300666540 /* NSObject+JKSObserving.h in Headers */,
04F315F41589DB090093739C /* JKSBlockUtilities.h in Headers */,
04F315F9158A0B4C0093739C /* JKSToolkit.h in Headers */,
);
Expand Down Expand Up @@ -427,6 +434,7 @@
04F315F51589DB090093739C /* JKSBlockUtilities.m in Sources */,
0490B39215B6AA6400EDEAB8 /* CATransaction+JKSAdditions.m in Sources */,
0434A9BF1622BD3C0068E5AF /* NSBezierPath+JKSAdditions.m in Sources */,
043B8C731627FF3300666540 /* NSObject+JKSObserving.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
37 changes: 37 additions & 0 deletions JKSToolkit/Categories/NSObject+JKSObserving.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// JKSObserving.h
// Frames
//
// Created by Johan Sørensen on 10/11/12.
// Copyright (c) 2012 Johan Sørensen. All rights reserved.
//

#import <Foundation/Foundation.h>

typedef void(^JKSObservingHandlerBlock)(id observedObject, NSDictionary *change);

@interface JKSObserving : NSObject
@property (strong, readonly) NSString *keyPath;
@property (weak, readonly) id observable;

+ (JKSObserving *)observingWithObject:(id)object
keyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
handler:(JKSObservingHandlerBlock)handlerBlock;
- (instancetype)initWithObject:(id)object
keyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
handler:(JKSObservingHandlerBlock)handlerBlock;
@end


@interface NSObject (JKSObservingAdditions)
/** Adds an observer for the keyPath
*
* @discussion The caller is responsible for controlling the lifetime of the observing, eg. deallocate
* the returned JKSObserving when the observing is no longer needed/wanted
*/
- (JKSObserving *)jks_observingForKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
handler:(JKSObservingHandlerBlock)handlerBlock;
@end
92 changes: 92 additions & 0 deletions JKSToolkit/Categories/NSObject+JKSObserving.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// JKSObserving.m
// Frames
//
// Created by Johan Sørensen on 10/11/12.
// Copyright (c) 2012 Johan Sørensen. All rights reserved.
//

#import "NSObject+JKSObserving.h"
#import <objc/runtime.h>

static void *JKSObservingKVOContext = &JKSObservingKVOContext;

@interface JKSObserving ()
@property (copy) JKSObservingHandlerBlock handler;
@end

@implementation JKSObserving
+ (JKSObserving *)observingWithObject:(id)object
keyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
handler:(JKSObservingHandlerBlock)handlerBlock
{
return [[JKSObserving alloc] initWithObject:object keyPath:keyPath options:options handler:handlerBlock];
}


- (instancetype)initWithObject:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options handler:(JKSObservingHandlerBlock)handlerBlock
{
NSParameterAssert(keyPath);
NSParameterAssert(handlerBlock);

if ((self = [super init])) {
_keyPath = keyPath;
_handler = [handlerBlock copy];
_observable = object;
[object addObserver:self forKeyPath:keyPath options:options context:JKSObservingKVOContext];
}
return self;
}


- (void)dealloc
{
[_observable removeObserver:self forKeyPath:_keyPath context:JKSObservingKVOContext];
}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == JKSObservingKVOContext) {
self.handler(self.observable, change);
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}

@end


//NSString *const kAssociatedObservingsKey = @"kAssociatedObservingsKey";
@implementation NSObject (JKSObservingAdditions)
- (JKSObserving *)jks_observingForKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
handler:(JKSObservingHandlerBlock)handlerBlock
{
JKSObserving *observing = [JKSObserving observingWithObject:self keyPath:keyPath options:options handler:handlerBlock];
return observing;
}


//- (void)jks_observeKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options handler:(JKSObservingHandlerBlock)handlerBlock
//{
// // TODO: raise if already added?
// JKSObserving *observer = [JKSObserving observingWithObject:self keyPath:keyPath options:options handler:handlerBlock];
// [[self jks_observings] addObject:observer];
//}
//
//// Big issue: associated objects area cleared after normal dealloc as part of object_dispose() (see WWDC 2011 session 322 (around 37:35))
//// Which means -[JKSObserving dealloc] is called too late and the KVO machinery spews a warning to the console
//- (NSMutableSet *)jks_observings
//{
// NSMutableSet *observings = objc_getAssociatedObject(self, &kAssociatedObservingsKey);
//
// if (!observings) {
// observings = [[NSMutableSet alloc] init];
// objc_setAssociatedObject(self, &kAssociatedObservingsKey, observings, OBJC_ASSOCIATION_RETAIN);
// }
//
// return observings;
//}
@end
1 change: 1 addition & 0 deletions JKSToolkit/JKSToolkit.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
#import "NSView+JKSAdditions.h"
#import "CATransaction+JKSAdditions.h"
#import "NSBezierPath+JKSAdditions.h"
#import "NSObject+JKSObserving.h"

0 comments on commit 9f1286f

Please sign in to comment.