Skip to content

Instantly share code, notes, and snippets.

@jeremytregunna
Last active December 27, 2015 06:09
Show Gist options
  • Save jeremytregunna/7279371 to your computer and use it in GitHub Desktop.
Save jeremytregunna/7279371 to your computer and use it in GitHub Desktop.
//
// NudgeModel.h
// NudgeFoundation
//
// Created by Jeremy Tregunna on 10/7/2013.
// Copyright (c) 2013 Jeremy Tregunna. All rights reserved.
//
#import <Foundation/Foundation.h>
#define weakify(o) try {} @finally {} \
__weak typeof(o) nf_weak_ ## o = o
#define strongify(o) try {} @finally {} \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
typeof(nf_weak_ ## o) o = nf_weak_ ## o \
_Pragma("clang diagnostic pop")
@interface NudgeModel : NSObject <NSSecureCoding, NSCopying, NSFastEnumeration>
- (instancetype)initWithAttributes:(NSDictionary*)attributes;
// Override this method and return a dictionary whose keys are the
// property name you define, and whose values are their key path to
// some external dictionary supplied during initialization.
- (NSDictionary*)keyPathsByPropertyKey;
@end
//
// NudgeModel.m
// NudgeFoundation
//
// Created by Jeremy Tregunna on 10/7/2013.
// Copyright (c) 2013 Jeremy Tregunna. All rights reserved.
//
#import "NudgeModel.h"
#import <objc/runtime.h>
@implementation NudgeModel
- (instancetype)initWithAttributes:(NSDictionary*)attributes
{
if((self = [super init]))
{
@weakify(self);
[[self keyPathsByPropertyKey] enumerateKeysAndObjectsUsingBlock:^(id key, NSString* keyPath, BOOL *stop) {
@strongify(self);
id object = [attributes valueForKeyPath:keyPath];
if(object != nil)
[self setValue:object forKey:key];
}];
}
return self;
}
#pragma mark - NSCoding
+ (BOOL)supportsSecureCoding
{
return YES;
}
- (id)initWithCoder:(NSCoder*)decoder
{
if((self = [super init]))
{
[[self keyPathsByPropertyKey] enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
id object = [decoder decodeObjectForKey:key];
[self setValue:object forKey:key];
}];
}
return self;
}
- (void)encodeWithCoder:(NSCoder*)coder
{
[[self keyPathsByPropertyKey] enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
id object = [self valueForKey:key];
if(object != nil)
[coder encodeObject:object forKey:key];
}];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone*)zone
{
typeof(self) obj = [[[self class] alloc] init];
@weakify(self);
[[self keyPathsByPropertyKey] enumerateKeysAndObjectsUsingBlock:^(id key, NSString* keyPath, BOOL *stop) {
@strongify(self);
id o = [self valueForKey:key];
[obj setValue:o forKey:key];
}];
return obj;
}
#pragma mark - NSFastEnumeration
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState*)state objects:(__unsafe_unretained id[])buffer count:(NSUInteger)len
{
return [[[self keyPathsByPropertyKey] allKeys] countByEnumeratingWithState:state objects:buffer count:len];
}
#pragma mark - NSObject
- (BOOL)isEqual:(NudgeModel*)model
{
if(self == model)
return YES;
if(![model isMemberOfClass:[self class]])
return NO;
__block BOOL result = YES;
@weakify(self);
[[self keyPathsByPropertyKey] enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
@strongify(self);
id o1 = [self valueForKey:key];
id o2 = [model valueForKey:key];
result = ((o1 == nil && o2 == nil) || [o1 isEqual:o2]);
if(!result)
*stop = YES;
}];
return result;
}
- (NSUInteger)hash
{
__block NSUInteger hash = 0;
@weakify(self);
[[self keyPathsByPropertyKey] enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
@strongify(self);
hash ^= [[self valueForKey:key] hash];
}];
return hash;
}
#pragma mark - Key paths
- (NSDictionary*)keyPathsByPropertyKey
{
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] userInfo:nil];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment