Skip to content
Newer
Older
100644 181 lines (133 sloc) 5.06 KB
97ebae9 @steipete SMModelObject is a very handy superclass for "Model Object" types.
authored Mar 6, 2011
1 #import "SMModelObject.h"
2 #import <objc/runtime.h>
3
4 // Holds metadata for subclasses of SMModelObject
5 static NSMutableDictionary *keyNames = nil, *nillableKeyNames = nil;
6
7 @implementation SMModelObject
8
9 // Before this class is first accessed, we'll need to build up our associated metadata, basically
10 // just a list of all our property names so we can quickly enumerate through them for various methods.
11 // Also we maintain a separate list of property names that can be set to nil (type ID) for fast dealloc.
12 + (void) initialize {
13
14 if (!keyNames)
15 keyNames = [NSMutableDictionary new], nillableKeyNames = [NSMutableDictionary new];
16
17 NSMutableArray *names = [NSMutableArray new], *nillableNames = [NSMutableArray new];
18
19 for (Class cls = self; cls != [SMModelObject class]; cls = [cls superclass]) {
20
21 unsigned int varCount;
22 Ivar *vars = class_copyIvarList(cls, &varCount);
23
24 for (int i = 0; i < varCount; i++) {
25 Ivar var = vars[i];
26 NSString *name = [[NSString alloc] initWithUTF8String:ivar_getName(var)];
27 [names addObject:name];
28
29 if (ivar_getTypeEncoding(var)[0] == _C_ID)
30 [nillableNames addObject:name];
31
32 [name release];
33 }
34
35 free(vars);
36 }
37
38 [keyNames setObject:names forKey:self];
39 [nillableKeyNames setObject:nillableNames forKey:self];
40 [names release], [nillableNames release];
41 }
42
43 - (NSArray *)allKeys {
44 return [keyNames objectForKey:[self class]];
45 }
46
47 - (NSArray *)nillableKeys {
48 return [nillableKeyNames objectForKey:[self class]];
49 }
50
51 // NSCoder implementation, for unarchiving
52 - (id) initWithCoder:(NSCoder *)aDecoder {
53 self = [super init];
54 if (self) {
55 for (NSString *name in [self allKeys])
56 [self setValue:[aDecoder decodeObjectForKey:name] forKey:name];
57 }
58 return self;
59 }
60
61 // NSCoder implementation, for archiving
62 - (void) encodeWithCoder:(NSCoder *)aCoder {
63
64 for (NSString *name in [self allKeys])
65 [aCoder encodeObject:[self valueForKey:name] forKey:name];
66 }
67
68 // Automatic dealloc.
69 - (void) dealloc {
70 for (NSString *name in [self nillableKeys])
71 [self setValue:nil forKey:name];
72
73 [super dealloc];
74 }
75
76 // NSCopying implementation
77 - (id) copyWithZone:(NSZone *)zone {
78
79 id copied = [[[self class] alloc] init];
80
81 for (NSString *name in [self allKeys])
82 [copied setValue:[self valueForKey:name] forKey:name];
83
84 return copied;
85 }
86
87 // We implement the NSFastEnumeration protocol to behave like an NSDictionary - the enumerated values are our property (key) names.
88 - (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len {
89 return [[self allKeys] countByEnumeratingWithState:state objects:stackbuf count:len];
90 }
91
92 // Override isEqual to compare model objects by value instead of just by pointer.
93 - (BOOL) isEqual:(id)other {
94
95 if ([other isKindOfClass:[self class]]) {
96
97 for (NSString *name in [self allKeys]) {
98 id a = [self valueForKey:name];
99 id b = [other valueForKey:name];
100
101 // extra check so a == b == nil is considered equal
102 if ((a || b) && ![a isEqual:b])
103 return NO;
104 }
105
106 return YES;
107 }
108 else return NO;
109 }
110
111 // Must override hash as well, this is taken directly from RMModelObject, basically
112 // classes with the same layout return the same number.
113 - (NSUInteger)hash {
114 return (NSUInteger)[self allKeys];
115 }
116
117 - (void)writeLineBreakToString:(NSMutableString *)string withTabs:(NSUInteger)tabCount {
118 [string appendString:@"\n"];
119 for (int i=0;i<tabCount;i++) [string appendString:@"\t"];
120 }
121
122 // Prints description in a nicely-formatted and indented manner.
123 - (void) writeToDescription:(NSMutableString *)description withIndent:(NSUInteger)indent {
124
125 [description appendFormat:@"<%@ %p", NSStringFromClass([self class]), self];
126
127 for (NSString *name in [self allKeys]) {
128
129 [self writeLineBreakToString:description withTabs:indent];
130
131 id object = [self valueForKey:name];
132
133 if ([object isKindOfClass:[SMModelObject class]]) {
134 [object writeToDescription:description withIndent:indent+1];
135 }
136 else if ([object isKindOfClass:[NSArray class]]) {
137
138 [description appendFormat:@"%@ =", name];
139
140 for (id child in object) {
141 [self writeLineBreakToString:description withTabs:indent+1];
142
143 if ([child isKindOfClass:[SMModelObject class]])
144 [child writeToDescription:description withIndent:indent+2];
145 else
146 [description appendString:[child description]];
147 }
148 }
149 else if ([object isKindOfClass:[NSDictionary class]]) {
150
151 [description appendFormat:@"%@ =", name];
152
153 for (id key in object) {
154 [self writeLineBreakToString:description withTabs:indent];
155 [description appendFormat:@"\t%@ = ",key];
156
157 id child = [object objectForKey:key];
158
159 if ([child isKindOfClass:[SMModelObject class]])
160 [child writeToDescription:description withIndent:indent+2];
161 else
162 [description appendString:[child description]];
163 }
164 }
165 else {
166 [description appendFormat:@"%@ = %@", name, object];
167 }
168 }
169
170 [description appendString:@">"];
171 }
172
173 // Override description for helpful debugging.
174 - (NSString *) description {
175 NSMutableString *description = [NSMutableString string];
176 [self writeToDescription:description withIndent:1];
177 return description;
178 }
179
180 @end
Something went wrong with that request. Please try again.