Skip to content

Instantly share code, notes, and snippets.

@hartbit
Last active August 29, 2015 13:56
Show Gist options
  • Save hartbit/9227494 to your computer and use it in GitHub Desktop.
Save hartbit/9227494 to your computer and use it in GitHub Desktop.
#import <Foundation/Foundation.h>
#import "ATPInfo.h"
@interface ATPDecorator : NSObject <ATPInfo>
@property (nonatomic, strong, readonly) NSString* fullName;
- (id)initWithInfo:(NSObject<ATPInfo>*)info;
@end
#import "ATPDecorator.h"
@interface ATPDecorator ()
@property (nonatomic, strong) NSObject<ATPInfo>* info;
@end
@implementation ATPDecorator
@dynamic firstName;
@dynamic lastName;
#pragma mark - Initialization
- (id)initWithInfo:(NSObject<ATPInfo>*)info
{
if (self = [super init]) {
_info = info;
}
return self;
}
#pragma mark - Properties
+ (NSSet*)keyPathsForValuesAffectingFullName
{
return [NSSet setWithObjects:@"info.firstName", @"info.lastName", nil];
}
- (NSString*)fullName
{
return [NSString stringWithFormat:@"%@ %@", self.info.firstName, self.info.lastName];
}
#pragma mark - Forwarding Methods
- (id)forwardingTargetForSelector:(SEL)selector
{
return [self.info respondsToSelector:selector] ? self.info : self;
}
- (BOOL)respondsToSelector:(SEL)selector
{
return [super respondsToSelector:selector] || [self.info respondsToSelector:selector];
}
- (BOOL)isKindOfClass:(Class)class
{
return [super isKindOfClass:class] || [self.info isKindOfClass:class];
}
- (void)addObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options context:(void*)context
{
if ([self respondsToKeyPath:keyPath]) {
[super addObserver:observer forKeyPath:keyPath options:options context:context];
} else {
[self.info addObserver:observer forKeyPath:keyPath options:options context:context];
}
}
- (void)removeObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath
{
if ([self respondsToKeyPath:keyPath]) {
[super removeObserver:observer forKeyPath:keyPath];
} else {
[self.info removeObserver:observer forKeyPath:keyPath];
}
}
- (void)removeObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath context:(void*)context
{
if ([self respondsToKeyPath:keyPath]) {
[super removeObserver:observer forKeyPath:keyPath context:context];
} else {
[self.info removeObserver:observer forKeyPath:keyPath context:context];
}
}
- (BOOL)respondsToKeyPath:(NSString*)keyPath
{
NSArray* keys = [keyPath componentsSeparatedByString:@"."];
NSString* firstKey = keys[0];
if (![super respondsToSelector:NSSelectorFromString(firstKey)]) {
return NO;
}
NSUInteger keyCount = [keys count];
if (keyCount > 1) {
id currentObject = [self valueForKey:firstKey];
for (NSUInteger index = 1; index < keyCount; index++) {
NSString* currentKey = keys[index];
if (![currentObject respondsToSelector:NSSelectorFromString(currentKey)]) {
return NO;
}
currentObject = [currentObject valueForKey:currentKey];
}
}
return YES;
}
@end
#import <Foundation/Foundation.h>
@protocol ATPInfo <NSObject>
@property (nonatomic, strong) NSString* firstName;
@property (nonatomic, strong) NSString* lastName;
@end
@interface ATPInfo : NSObject <ATPInfo>
@end
#import "ATPInfo.h"
@implementation ATPInfo
@synthesize firstName = _firstName;
@synthesize lastName = _lastName;
@end
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Cannot update for observer <ATPAppDelegate 0x8a1f5f0> for the key path "fullName.length" from <ATPDecorator 0x8a53f70>, most likely because the value for the key "fullName" has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the ATPDecorator class.'
ATPInfo* info = [ATPInfo new];
info.firstName = @"David";
info.lastName = @"Hart";
ATPDecorator* decorator = [[ATPDecorator alloc] initWithInfo:info];
[decorator addObserver:self forKeyPath:@"firstName" options:NSKeyValueObservingOptionNew context:kKVOContext];
[decorator addObserver:self forKeyPath:@"firstName.length" options:NSKeyValueObservingOptionNew context:kKVOContext];
[decorator addObserver:self forKeyPath:@"fullName" options:NSKeyValueObservingOptionNew context:kKVOContext];
[decorator addObserver:self forKeyPath:@"fullName.length" options:NSKeyValueObservingOptionNew context:kKVOContext];
info.firstName = @"Stephen";
decorator.firstName = @"Charlotte";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment