Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Updated to version 2.4

  • Loading branch information...
commit 7b66463095614cad6ea3ad99dbdccf87306a1eb6 1 parent 92493bc
@nicklockwood authored
Showing with 455 additions and 2,138 deletions.
  1. +2 −13 BaseModel/BaseModel.h
  2. +114 −136 BaseModel/BaseModel.m
  3. +8 −3 Examples/AutoTodoList/AutoCoding/AutoCoding.h
  4. +74 −48 Examples/AutoTodoList/AutoCoding/AutoCoding.m
  5. +6 −0 Examples/AutoTodoList/AutoTodoList.xcodeproj/project.pbxproj
  6. +1 −6 Examples/AutoTodoList/Classes/NewItemViewController.m
  7. +1 −1  Examples/AutoTodoList/Classes/TodoItem.h
  8. +0 −5 Examples/AutoTodoList/Classes/TodoItem.m
  9. +1 −1  Examples/AutoTodoList/Classes/TodoList.h
  10. +0 −5 Examples/AutoTodoList/Classes/TodoList.m
  11. +2 −2 Examples/AutoTodoList/Classes/TodoListAppDelegate.h
  12. +0 −6 Examples/AutoTodoList/Classes/TodoListAppDelegate.m
  13. +1 −2  Examples/AutoTodoList/Classes/TodoListViewController.m
  14. +4 −4 Examples/AutoTodoList/main.m
  15. +1 −6 Examples/CryptoTodoList/Classes/NewItemViewController.m
  16. +1 −1  Examples/CryptoTodoList/Classes/TodoItem.h
  17. +0 −5 Examples/CryptoTodoList/Classes/TodoItem.m
  18. +1 −1  Examples/CryptoTodoList/Classes/TodoList.h
  19. +0 −5 Examples/CryptoTodoList/Classes/TodoList.m
  20. +2 −2 Examples/CryptoTodoList/Classes/TodoListAppDelegate.h
  21. +0 −6 Examples/CryptoTodoList/Classes/TodoListAppDelegate.m
  22. +1 −2  Examples/CryptoTodoList/Classes/TodoListViewController.m
  23. +6 −0 Examples/CryptoTodoList/CryptoTodoList.xcodeproj/project.pbxproj
  24. +4 −4 Examples/CryptoTodoList/main.m
  25. +1 −6 Examples/HRTodoList/Classes/NewItemViewController.m
  26. +1 −1  Examples/HRTodoList/Classes/TodoItem.h
  27. +0 −5 Examples/HRTodoList/Classes/TodoItem.m
  28. +1 −1  Examples/HRTodoList/Classes/TodoList.h
  29. +0 −5 Examples/HRTodoList/Classes/TodoList.m
  30. +2 −2 Examples/HRTodoList/Classes/TodoListAppDelegate.h
  31. +0 −6 Examples/HRTodoList/Classes/TodoListAppDelegate.m
  32. +1 −2  Examples/HRTodoList/Classes/TodoListViewController.m
  33. +9 −7 Examples/HRTodoList/HRCoder/HRCoder.h
  34. +148 −90 Examples/HRTodoList/HRCoder/HRCoder.m
  35. +7 −1 Examples/HRTodoList/HRTodoList.xcodeproj/project.pbxproj
  36. +1 −0  Examples/HRTodoList/HRTodoList.xcodeproj/xcshareddata/xcschemes/HRTodoList.xcscheme
  37. +4 −4 Examples/HRTodoList/main.m
  38. +1 −6 Examples/TodoList/Classes/NewItemViewController.m
  39. +1 −1  Examples/TodoList/Classes/TodoItem.h
  40. +0 −5 Examples/TodoList/Classes/TodoItem.m
  41. +1 −1  Examples/TodoList/Classes/TodoList.h
  42. +0 −5 Examples/TodoList/Classes/TodoList.m
  43. +2 −2 Examples/TodoList/Classes/TodoListAppDelegate.h
  44. +0 −6 Examples/TodoList/Classes/TodoListAppDelegate.m
  45. +1 −2  Examples/TodoList/Classes/TodoListViewController.m
  46. +6 −0 Examples/TodoList/TodoList.xcodeproj/project.pbxproj
  47. +4 −4 Examples/TodoList/main.m
  48. +1 −1  LICENCE.md
  49. +26 −39 README.md
  50. +7 −0 RELEASE NOTES.md
  51. +0 −15 Tests/ARC Test/Classes/NewItemViewController.h
  52. +0 −50 Tests/ARC Test/Classes/NewItemViewController.m
  53. +0 −394 Tests/ARC Test/Classes/NewItemViewController.xib
  54. +0 −21 Tests/ARC Test/Classes/TodoItem.h
  55. +0 −46 Tests/ARC Test/Classes/TodoItem.m
  56. +0 −18 Tests/ARC Test/Classes/TodoList.h
  57. +0 −40 Tests/ARC Test/Classes/TodoList.m
  58. +0 −18 Tests/ARC Test/Classes/TodoListAppDelegate.h
  59. +0 −24 Tests/ARC Test/Classes/TodoListAppDelegate.m
  60. +0 −16 Tests/ARC Test/Classes/TodoListViewController.h
  61. +0 −89 Tests/ARC Test/Classes/TodoListViewController.m
  62. +0 −366 Tests/ARC Test/MainWindow.xib
  63. +0 −30 Tests/ARC Test/TodoList-Info.plist
  64. +0 −10 Tests/ARC Test/TodoList.plist
  65. +0 −321 Tests/ARC Test/TodoList.xcodeproj/project.pbxproj
  66. +0 −189 Tests/ARC Test/TodoListViewController.xib
  67. +0 −8 Tests/ARC Test/TodoList_Prefix.pch
  68. +0 −17 Tests/ARC Test/main.m
View
15 BaseModel/BaseModel.h 100644 → 100755
@@ -1,7 +1,7 @@
//
// BaseModel.h
//
-// Version 2.3.5
+// Version 2.4
//
// Created by Nick Lockwood on 25/06/2011.
// Copyright 2011 Charcoal Design
@@ -45,15 +45,13 @@ extern NSString *const BaseModelSharedInstanceUpdatedNotification;
//loading sequence:
//setUp called first
-//then setWithDictionary/Array/String if resource file exists
+//then setWithDictionary/Array/String/etc if resource file exists
//then setWithCoder if save file exists
- (void)setUp;
- (void)setWithDictionary:(NSDictionary *)dict;
- (void)setWithArray:(NSArray *)array;
- (void)setWithString:(NSString *)string;
-- (void)setWithNumber:(NSNumber *)number;
-- (void)setWithData:(NSData *)data;
- (void)setWithCoder:(NSCoder *)coder;
//coding
@@ -110,13 +108,4 @@ extern NSString *const BaseModelSharedInstanceUpdatedNotification;
//identifiers and filenames for model objects
+ (NSString *)newUniqueIdentifier;
-#ifdef BASEMODEL_ENABLE_UNIQUE_ID
-
-//optional uniqueID property
-//you can enable this by adding BASEMODEL_ENABLE_UNIQUE_ID
-//to your preprocessor macros in the project build settings
-@property (nonatomic, strong) NSString *uniqueID;
-
-#endif
-
@end
View
250 BaseModel/BaseModel.m 100644 → 100755
@@ -1,7 +1,7 @@
//
// BaseModel.m
//
-// Version 2.3.5
+// Version 2.4
//
// Created by Nick Lockwood on 25/06/2011.
// Copyright 2011 Charcoal Design
@@ -30,39 +30,15 @@
// 3. This notice may not be removed or altered from any source distribution.
//
-//
-// ARC Helper
-//
-// Version 2.1
-//
-// Created by Nick Lockwood on 05/01/2012.
-// Copyright 2012 Charcoal Design
-//
-// Distributed under the permissive zlib license
-// Get the latest version from here:
-//
-// https://gist.github.com/1563325
-//
-
-#ifndef ah_retain
-#if __has_feature(objc_arc)
-#define ah_retain self
-#define ah_dealloc self
-#define release self
-#define autorelease self
-#else
-#define ah_retain retain
-#define ah_dealloc dealloc
-#define __bridge
-#endif
-#endif
-
-// ARC Helper ends
-
#import "BaseModel.h"
#import <objc/message.h>
-#import <objc/runtime.h>
+
+
+#import <Availability.h>
+#if !__has_feature(objc_arc)
+#error This class requires automatic reference counting
+#endif
NSString *const BaseModelSharedInstanceUpdatedNotification = @"BaseModelSharedInstanceUpdatedNotification";
@@ -77,7 +53,7 @@ @implementation BaseModel
#pragma mark -
#pragma mark Private utility methods
-+ (NSString *)resourceFilePath:(NSString *)path
++ (NSString *)BaseModel_resourceFilePath:(NSString *)path
{
//check if the path is a full path or not
if (![path isAbsolutePath])
@@ -87,12 +63,12 @@ + (NSString *)resourceFilePath:(NSString *)path
return path;
}
-+ (NSString *)resourceFilePath
++ (NSString *)BaseModel_resourceFilePath
{
- return [self resourceFilePath:[self resourceFile]];
+ return [self BaseModel_resourceFilePath:[self resourceFile]];
}
-+ (NSString *)saveFilePath:(NSString *)path
++ (NSString *)BaseModel_saveFilePath:(NSString *)path
{
//check if the path is a full path or not
if (![path isAbsolutePath])
@@ -122,39 +98,42 @@ + (NSString *)saveFilePath:(NSString *)path
return path;
}
-+ (NSString *)saveFilePath
++ (NSString *)BaseModel_saveFilePath
{
- return [self saveFilePath:[self saveFile]];
+ return [self BaseModel_saveFilePath:[self saveFile]];
}
static NSMutableDictionary *classValues = nil;
-+ (id)classPropertyForKey:(NSString *)key
++ (id)BaseModel_classPropertyForKey:(NSString *)key
{
NSString *className = NSStringFromClass(self);
- return [[classValues objectForKey:className] objectForKey:key];
+ return classValues[className][key];
}
-+ (void)setClassProperty:(id)property forKey:(NSString *)key
++ (void)BaseModel_setClassProperty:(id)property forKey:(NSString *)key
{
- NSString *className = NSStringFromClass(self);
- if (!classValues)
- {
- classValues = [NSMutableDictionary dictionary];
- }
- NSMutableDictionary *values = [classValues objectForKey:className];
- if (!values)
- {
- values = [NSMutableDictionary dictionary];
- [classValues setObject:values forKey:className];
- }
- if (property)
+ @synchronized ([BaseModel class])
{
- [values setObject:property forKey:key];
- }
- else
- {
- [values removeObjectForKey:key];
+ NSString *className = NSStringFromClass(self);
+ if (!classValues)
+ {
+ classValues = [[NSMutableDictionary alloc] init];
+ }
+ NSMutableDictionary *values = classValues[className];
+ if (!values)
+ {
+ values = [NSMutableDictionary dictionary];
+ classValues[className] = values;
+ }
+ if (property)
+ {
+ values[key] = property;
+ }
+ else
+ {
+ [values removeObjectForKey:key];
+ }
}
}
@@ -164,51 +143,60 @@ + (void)setClassProperty:(id)property forKey:(NSString *)key
+ (void)setSharedInstance:(BaseModel *)instance
{
- if (instance && ![instance isKindOfClass:self])
- {
- [NSException raise:NSGenericException format:@"setSharedInstance: instance class does not match"];
- }
- id oldInstance = [self classPropertyForKey:BaseModelSharedInstanceKey];
- [self setClassProperty:instance forKey:BaseModelSharedInstanceKey];
- if (oldInstance)
+ @synchronized ([self class])
{
- [[NSNotificationCenter defaultCenter] postNotificationName:BaseModelSharedInstanceUpdatedNotification object:oldInstance];
+ if (instance && ![instance isKindOfClass:self])
+ {
+ [NSException raise:NSGenericException format:@"setSharedInstance: instance class does not match"];
+ }
+ id oldInstance = [self BaseModel_classPropertyForKey:BaseModelSharedInstanceKey];
+ [self BaseModel_setClassProperty:instance forKey:BaseModelSharedInstanceKey];
+ if (oldInstance)
+ {
+ [[NSNotificationCenter defaultCenter] postNotificationName:BaseModelSharedInstanceUpdatedNotification object:oldInstance];
+ }
}
}
+ (BOOL)hasSharedInstance
{
- return [self classPropertyForKey:BaseModelSharedInstanceKey] != nil;
+ return [self BaseModel_classPropertyForKey:BaseModelSharedInstanceKey] != nil;
}
+ (instancetype)sharedInstance
{
- id instance = [self classPropertyForKey:BaseModelSharedInstanceKey];
- if (instance == nil)
+ @synchronized ([self class])
{
- //load or create instance
- [self reloadSharedInstance];
-
- //get loaded instance
- instance = [self classPropertyForKey:BaseModelSharedInstanceKey];
+ id instance = [self BaseModel_classPropertyForKey:BaseModelSharedInstanceKey];
+ if (instance == nil)
+ {
+ //load or create instance
+ [self reloadSharedInstance];
+
+ //get loaded instance
+ instance = [self BaseModel_classPropertyForKey:BaseModelSharedInstanceKey];
+ }
+ return instance;
}
- return instance;
}
+ (void)reloadSharedInstance
{
- id instance = nil;
-
- //try loading previously saved version
- instance = [self instanceWithContentsOfFile:[self saveFilePath]];
- if (instance == nil)
+ @synchronized ([self class])
{
- //construct a new instance
- instance = [self instance];
+ id instance = nil;
+
+ //try loading previously saved version
+ instance = [self instanceWithContentsOfFile:[self BaseModel_saveFilePath]];
+ if (instance == nil)
+ {
+ //construct a new instance
+ instance = [self instance];
+ }
+
+ //set singleton
+ [self setSharedInstance:instance];
}
-
- //set singleton
- [self setSharedInstance:instance];
}
+ (NSString *)resourceFile
@@ -230,10 +218,10 @@ - (BOOL)useHRCoderIfAvailable
- (void)save
{
- if ([[self class] classPropertyForKey:BaseModelSharedInstanceKey] == self)
+ if ([[self class] BaseModel_classPropertyForKey:BaseModelSharedInstanceKey] == self)
{
//shared (singleton) instance
- [self writeToFile:[[self class] saveFilePath] atomically:YES];
+ [self writeToFile:[[self class] BaseModel_saveFilePath] atomically:YES];
}
else
{
@@ -253,35 +241,31 @@ - (void)setUp
+ (instancetype)instance
{
- return [[[self alloc] init] autorelease];
+ return [[self alloc] init];
}
- (instancetype)init
{
@synchronized ([self class])
{
- if (![[[self class] classPropertyForKey:BaseModelLoadingFromResourceFileKey] boolValue])
+ if (![[[self class] BaseModel_classPropertyForKey:BaseModelLoadingFromResourceFileKey] boolValue])
{
//attempt to load from resource file
- [[self class] setClassProperty:[NSNumber numberWithBool:YES] forKey:BaseModelLoadingFromResourceFileKey];
- id object = [[[self class] alloc] initWithContentsOfFile:[[self class] resourceFilePath]];
- [[self class] setClassProperty:nil forKey:BaseModelLoadingFromResourceFileKey];
+ [[self class] BaseModel_setClassProperty:@YES forKey:BaseModelLoadingFromResourceFileKey];
+ id object = [[[self class] alloc] initWithContentsOfFile:[[self class] BaseModel_resourceFilePath]];
+ [[self class] BaseModel_setClassProperty:nil forKey:BaseModelLoadingFromResourceFileKey];
if (object)
{
- [self release];
- self = object;
- return self;
+ return ((self = object));
}
}
if ((self = [super init]))
{
-
-#ifdef DEBUG
if ([self class] == [BaseModel class])
{
[NSException raise:NSGenericException format:@"BaseModel class is abstract and should be subclassed rather than instantiated directly"];
}
-#endif
+
[self setUp];
}
return self;
@@ -291,7 +275,7 @@ - (instancetype)init
+ (instancetype)instanceWithObject:(id)object
{
//return nil if object is nil
- return object? [[[self alloc] initWithObject:object] autorelease]: nil;
+ return object? [[self alloc] initWithObject:object]: nil;
}
- (NSString *)setterNameForClass:(Class)class
@@ -344,7 +328,7 @@ + (NSArray *)instancesWithArray:(NSArray *)array
+ (instancetype)instanceWithCoder:(NSCoder *)decoder
{
//return nil if coder is nil
- return decoder? [[[self alloc] initWithCoder:decoder] autorelease]: nil;
+ return decoder? [[self alloc] initWithCoder:decoder]: nil;
}
- (instancetype)initWithCoder:(NSCoder *)decoder
@@ -370,15 +354,15 @@ + (instancetype)instanceWithContentsOfFile:(NSString *)filePath
if (![path isAbsolutePath])
{
//try resources
- path = [self resourceFilePath:filePath];
+ path = [self BaseModel_resourceFilePath:filePath];
if (![[NSFileManager defaultManager] fileExistsAtPath:path])
{
//try application support
- path = [self saveFilePath:filePath];
+ path = [self BaseModel_saveFilePath:filePath];
}
}
-
- return [[[self alloc] initWithContentsOfFile:path] autorelease];
+
+ return [[self alloc] initWithContentsOfFile:path];
}
- (instancetype)initWithContentsOfFile:(NSString *)filePath
@@ -393,7 +377,7 @@ - (instancetype)initWithContentsOfFile:(NSString *)filePath
}
//check cache for existing instance
- //only cache files inside the main bundle as they are immutable
+ //only cache files inside the main bundle as they are immutable
BOOL isResourceFile = [filePath hasPrefix:[[NSBundle mainBundle] bundlePath]];
if (isResourceFile)
{
@@ -418,26 +402,43 @@ - (instancetype)initWithContentsOfFile:(NSString *)filePath
data = [NSData dataWithContentsOfFile:filePath];
}
- //attempt to deserialise data as a plist
if (data)
{
+ //attempt to deserialise data as a plist
NSPropertyListFormat format;
NSPropertyListReadOptions options = NSPropertyListMutableContainersAndLeaves;
if (!(object = [NSPropertyListSerialization propertyListWithData:data options:options format:&format error:NULL]))
{
- //data is not a plist
- object = data;
+ //attempt to deserialise data as json
+ Class FXJSONClass = NSClassFromString(@"FXJSON");
+ if (FXJSONClass)
+ {
+ object = objc_msgSend(FXJSONClass, @selector(objectWithJSONData:), data);
+ }
+ else if ([NSJSONSerialization class])
+ {
+ object = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:NULL];
+ }
+ else
+ {
+ [NSException raise:NSGenericException format:@"No JSON deserialisation class found. Add FXJSON (https://github.com/nicklockwood/FXJSON) to the project to resolve this."];
+ }
+ if (!object)
+ {
+ //data is not a known serialisation format
+ object = data;
+ }
}
}
}
-
+
//success?
if (object)
{
//check if object is an NSCoded archive
if ([object respondsToSelector:@selector(objectForKey:)])
{
- if ([object objectForKey:@"$archiver"])
+ if (object[@"$archiver"])
{
if (isResourceFile)
{
@@ -464,7 +465,7 @@ - (instancetype)initWithContentsOfFile:(NSString *)filePath
//unarchive object
Class HRCoderClass = NSClassFromString(@"HRCoder");
NSString *classNameKey = [HRCoderClass valueForKey:@"classNameKey"];
- if ([object objectForKey:classNameKey])
+ if (object[classNameKey])
{
object = objc_msgSend(HRCoderClass, @selector(unarchiveObjectWithPlist:), object);
}
@@ -473,8 +474,7 @@ - (instancetype)initWithContentsOfFile:(NSString *)filePath
if ([object isKindOfClass:[self class]])
{
//return object
- [self release];
- return ((self = [object ah_retain]));
+ return ((self = object));
}
}
else if (isResourceFile)
@@ -493,7 +493,6 @@ - (instancetype)initWithContentsOfFile:(NSString *)filePath
}
//failed to load
- [self release];
return ((self = nil));
}
@@ -516,7 +515,7 @@ - (void)writeToFile:(NSString *)path atomically:(BOOL)atomically
{
data = [NSKeyedArchiver archivedDataWithRootObject:self];
}
- [data writeToFile:[[self class] saveFilePath:path] atomically:YES];
+ [data writeToFile:[[self class] BaseModel_saveFilePath:path] atomically:YES];
}
@@ -528,28 +527,7 @@ + (NSString *)newUniqueIdentifier
CFUUIDRef uuid = CFUUIDCreate(NULL);
CFStringRef identifier = CFUUIDCreateString(NULL, uuid);
CFRelease(uuid);
- return [CFBridgingRelease(identifier) ah_retain];
-}
-
-#ifdef BASEMODEL_ENABLE_UNIQUE_ID
-
-@synthesize uniqueID = _uniqueID;
-
-- (NSString *)uniqueID
-{
- if (_uniqueID == nil)
- {
- _uniqueID = [[self class] newUniqueIdentifier];
- }
- return _uniqueID;
+ return CFBridgingRelease(identifier);
}
-- (void)dealloc
-{
- [_uniqueID release];
- [super ah_dealloc];
-}
-
-#endif
-
@end
View
11 Examples/AutoTodoList/AutoCoding/AutoCoding.h
@@ -1,7 +1,7 @@
//
// AutoCoding.h
//
-// Version 1.2.1
+// Version 1.3
//
// Created by Nick Lockwood on 19/11/2011.
// Copyright (c) 2011 Charcoal Design
@@ -34,10 +34,15 @@
//coding
-- (NSArray *)codableKeys;
-- (NSArray *)uncodableKeys;
++ (NSArray *)codableKeys;
++ (NSArray *)uncodableKeys;
- (void)setWithCoder:(NSCoder *)aDecoder;
+//property access
+
+- (NSArray *)codableKeys;
+- (NSDictionary *)dictionaryRepresentation;
+
//loading / saving
+ (instancetype)objectWithContentsOfFile:(NSString *)path;
View
122 Examples/AutoTodoList/AutoCoding/AutoCoding.m
@@ -1,7 +1,7 @@
//
// AutoCoding.m
//
-// Version 1.2.1
+// Version 1.3
//
// Created by Nick Lockwood on 19/11/2011.
// Copyright (c) 2011 Charcoal Design
@@ -86,7 +86,7 @@ - (BOOL)writeToFile:(NSString *)filePath atomically:(BOOL)useAuxiliaryFile
return [data writeToFile:filePath atomically:useAuxiliaryFile];
}
-- (NSArray *)codableKeys
++ (NSArray *)codableKeys
{
@synchronized([NSObject class])
{
@@ -96,78 +96,103 @@ - (NSArray *)codableKeys
keysByClass = [[NSMutableDictionary alloc] init];
}
- Class class = [self class];
- NSString *className = NSStringFromClass(class);
+ NSString *className = NSStringFromClass(self);
NSMutableArray *codableKeys = [keysByClass objectForKey:className];
if (codableKeys == nil)
{
+ //deprecated
+ if ([self instancesRespondToSelector:@selector(uncodableKeys)])
+ {
+ NSLog(@"AutoCoding Warning: uncodableKeys instance method is no longer supported. Use class method instead.");
+ }
+
codableKeys = [NSMutableArray array];
- while (class != [NSObject class])
+ unsigned int propertyCount;
+ objc_property_t *properties = class_copyPropertyList(self, &propertyCount);
+ for (int i = 0; i < propertyCount; i++)
{
- unsigned int propertyCount;
- objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
- for (int i = 0; i < propertyCount; i++)
+ //get property
+ objc_property_t property = properties[i];
+ const char *propertyName = property_getName(property);
+ NSString *key = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
+
+ //see if there is a backing ivar
+ char *ivar = property_copyAttributeValue(property, "V");
+ if (ivar)
{
- //get property
- objc_property_t property = properties[i];
- const char *propertyName = property_getName(property);
- NSString *key = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
-
//check if read-only
- BOOL readonly = NO;
- const char *attributes = property_getAttributes(property);
- NSString *encoding = [NSString stringWithCString:attributes encoding:NSUTF8StringEncoding];
- if ([[encoding componentsSeparatedByString:@","] containsObject:@"R"])
+ char *readonly = property_copyAttributeValue(property, "R");
+ if (readonly)
{
- readonly = YES;
-
- //see if there is a backing ivar with a KVC-compliant name
- NSRange iVarRange = [encoding rangeOfString:@",V"];
- if (iVarRange.location != NSNotFound)
+ //check if ivar has KVC-compliant name
+ NSString *ivarName = [NSString stringWithFormat:@"%s", ivar];
+ if ([ivarName isEqualToString:key] ||
+ [ivarName isEqualToString:[@"_" stringByAppendingString:key]])
{
- NSString *iVarName = [encoding substringFromIndex:iVarRange.location + 2];
- if ([iVarName isEqualToString:key] ||
- [iVarName isEqualToString:[@"_" stringByAppendingString:key]])
- {
- //setValue:forKey: will still work
- readonly = NO;
- }
+ //no setter, but setValue:forKey: will still work
+ [codableKeys addObject:key];
}
+ free(readonly);
}
-
- if (!readonly)
+ else if (![[self uncodableKeys] containsObject:key])
{
- //exclude read-only properties
+ //there is a setter method so setValue:forKey: will work
[codableKeys addObject:key];
}
+ free(ivar);
}
- free(properties);
- class = [class superclass];
}
- [keysByClass setObject:codableKeys forKey:className];
+ free(properties);
+ [keysByClass setObject:[NSArray arrayWithArray:codableKeys] forKey:className];
}
-
- NSArray *uncodableKeys = [self uncodableKeys];
- return [codableKeys filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
- return ![uncodableKeys containsObject:evaluatedObject];
- }]];
+ return codableKeys;
}
}
-- (NSArray *)uncodableKeys
++ (NSArray *)uncodableKeys
{
return nil;
}
-- (void)setWithCoder:(NSCoder *)aDecoder
+- (NSArray *)codableKeys
{
- @synchronized(self)
+ @synchronized([NSObject class])
{
- for (NSString *key in [self codableKeys])
+ static NSMutableDictionary *keysByClass = nil;
+ if (keysByClass == nil)
+ {
+ keysByClass = [[NSMutableDictionary alloc] init];
+ }
+
+ NSString *className = NSStringFromClass([self class]);
+ NSArray *codableKeys = [keysByClass objectForKey:className];
+ if (codableKeys == nil)
{
- id object = [aDecoder decodeObjectForKey:key];
- if (object) [self setValue:object forKey:key];
+ NSMutableSet *set = [NSMutableSet set];
+ Class class = [self class];
+ while (class != [NSObject class])
+ {
+ [set addObjectsFromArray:[class codableKeys]];
+ class = [class superclass];
+ }
+ codableKeys = [set allObjects];
+ [keysByClass setObject:codableKeys forKey:className];
}
+ return codableKeys;
+ }
+}
+
+- (NSDictionary *)dictionaryRepresentation
+{
+ return [self dictionaryWithValuesForKeys:[self codableKeys]];
+}
+
+- (void)setWithCoder:(NSCoder *)aDecoder
+{
+ for (NSString *key in [self codableKeys])
+ {
+ id object = [aDecoder decodeObjectForKey:key];
+ if (object) [self setValue:object forKey:key];
}
}
@@ -182,13 +207,14 @@ - (void)encodeWithCoder:(NSCoder *)aCoder
for (NSString *key in [self codableKeys])
{
id object = [self valueForKey:key];
- [aCoder encodeObject:object forKey:key];
+ if (object) [aCoder encodeObject:object forKey:key];
}
}
- (instancetype)copyWithZone:(NSZone *)zone
{
- NSObject *copy = [[[self class] allocWithZone:zone] init];
+ Class class = [self class];
+ NSObject *copy = [[class allocWithZone:zone] init];
for (NSString *key in [self codableKeys])
{
id object = [self valueForKey:key];
View
6 Examples/AutoTodoList/AutoTodoList.xcodeproj/project.pbxproj
@@ -259,6 +259,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -275,6 +276,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = AutoTodoList_Prefix.pch;
@@ -290,8 +292,10 @@
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
+ RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
};
name = Debug;
@@ -302,8 +306,10 @@
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
+ RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
};
name = Release;
View
7 Examples/AutoTodoList/Classes/NewItemViewController.m
@@ -13,7 +13,7 @@
@interface NewItemViewController()
-@property (nonatomic, retain) TodoItem *item;
+@property (nonatomic, strong) TodoItem *item;
@end
@@ -46,10 +46,5 @@ - (void)textViewDidChange:(UITextView *)textView
#pragma mark -
#pragma mark Cleanup
-- (void)dealloc
-{
- [item release];
- [super dealloc];
-}
@end
View
2  Examples/AutoTodoList/Classes/TodoItem.h
@@ -13,7 +13,7 @@
@interface TodoItem : BaseModel
-@property (nonatomic, retain) NSString *label;
+@property (nonatomic, strong) NSString *label;
@property (nonatomic, assign) BOOL checked;
+ (instancetype)instanceWithLabel:(NSString *)label;
View
5 Examples/AutoTodoList/Classes/TodoItem.m
@@ -34,10 +34,5 @@ - (void)save
//note: we've not implemented the NSCoding methods
//the AutoCoding library takes care of this for us
-- (void)dealloc
-{
- [label release];
- [super dealloc];
-}
@end
View
2  Examples/AutoTodoList/Classes/TodoList.h
@@ -13,6 +13,6 @@
@interface TodoList : BaseModel
-@property (nonatomic, retain) NSMutableArray *items;
+@property (nonatomic, strong) NSMutableArray *items;
@end
View
5 Examples/AutoTodoList/Classes/TodoList.m
@@ -28,10 +28,5 @@ - (void)setWithArray:(NSArray *)array
//note: we've not implemented the NSCoding methods
//the AutoCoding library takes care of this for us
-- (void)dealloc
-{
- [items release];
- [super dealloc];
-}
@end
View
4 Examples/AutoTodoList/Classes/TodoListAppDelegate.h
@@ -11,8 +11,8 @@
@interface TodoListAppDelegate : NSObject <UIApplicationDelegate>
-@property (nonatomic, retain) IBOutlet UIWindow *window;
-@property (nonatomic, retain) IBOutlet UIViewController *viewController;
+@property (nonatomic, strong) IBOutlet UIWindow *window;
+@property (nonatomic, strong) IBOutlet UIViewController *viewController;
@end
View
6 Examples/AutoTodoList/Classes/TodoListAppDelegate.m
@@ -19,12 +19,6 @@ - (void)applicationDidFinishLaunching:(UIApplication *)application
[window makeKeyAndVisible];
}
-- (void)dealloc
-{
- [viewController release];
- [window release];
- [super dealloc];
-}
@end
View
3  Examples/AutoTodoList/Classes/TodoListViewController.m
@@ -31,7 +31,6 @@ - (IBAction)createNewItem
{
UIViewController *viewController = [[NewItemViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
- [viewController release];
}
#pragma mark -
@@ -73,7 +72,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellType];
if (cell == nil)
{
- cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellType] autorelease];
+ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellType];
}
TodoItem *item = [[TodoList sharedInstance].items objectAtIndex:indexPath.row];
View
8 Examples/AutoTodoList/main.m
@@ -10,8 +10,8 @@
int main(int argc, char *argv[]) {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- int retVal = UIApplicationMain(argc, argv, nil, nil);
- [pool release];
- return retVal;
+ @autoreleasepool {
+ int retVal = UIApplicationMain(argc, argv, nil, nil);
+ return retVal;
+ }
}
View
7 Examples/CryptoTodoList/Classes/NewItemViewController.m
@@ -13,7 +13,7 @@
@interface NewItemViewController()
-@property (nonatomic, retain) TodoItem *item;
+@property (nonatomic, strong) TodoItem *item;
@end
@@ -46,10 +46,5 @@ - (void)textViewDidChange:(UITextView *)textView
#pragma mark -
#pragma mark Cleanup
-- (void)dealloc
-{
- [item release];
- [super dealloc];
-}
@end
View
2  Examples/CryptoTodoList/Classes/TodoItem.h
@@ -13,7 +13,7 @@
@interface TodoItem : BaseModel
-@property (nonatomic, retain) NSString *label;
+@property (nonatomic, strong) NSString *label;
@property (nonatomic, assign) BOOL checked;
+ (instancetype)instanceWithLabel:(NSString *)label;
View
5 Examples/CryptoTodoList/Classes/TodoItem.m
@@ -43,11 +43,6 @@ - (void)save
[[TodoList sharedInstance] save];
}
-- (void)dealloc
-{
- [label release];
- [super dealloc];
-}
@end
View
2  Examples/CryptoTodoList/Classes/TodoList.h
@@ -13,6 +13,6 @@
@interface TodoList : BaseModel
-@property (nonatomic, retain) NSMutableArray *items;
+@property (nonatomic, strong) NSMutableArray *items;
@end
View
5 Examples/CryptoTodoList/Classes/TodoList.m
@@ -46,10 +46,5 @@ - (void)encodeWithCoder:(NSCoder *)aCoder
[aCoder encodeObject:items forKey:@"items"];
}
-- (void)dealloc
-{
- [items release];
- [super dealloc];
-}
@end
View
4 Examples/CryptoTodoList/Classes/TodoListAppDelegate.h
@@ -11,8 +11,8 @@
@interface TodoListAppDelegate : NSObject <UIApplicationDelegate>
-@property (nonatomic, retain) IBOutlet UIWindow *window;
-@property (nonatomic, retain) IBOutlet UIViewController *viewController;
+@property (nonatomic, strong) IBOutlet UIWindow *window;
+@property (nonatomic, strong) IBOutlet UIViewController *viewController;
@end
View
6 Examples/CryptoTodoList/Classes/TodoListAppDelegate.m
@@ -19,12 +19,6 @@ - (void)applicationDidFinishLaunching:(UIApplication *)application
[window makeKeyAndVisible];
}
-- (void)dealloc
-{
- [viewController release];
- [window release];
- [super dealloc];
-}
@end
View
3  Examples/CryptoTodoList/Classes/TodoListViewController.m
@@ -31,7 +31,6 @@ - (IBAction)createNewItem
{
UIViewController *viewController = [[NewItemViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
- [viewController release];
}
#pragma mark -
@@ -73,7 +72,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellType];
if (cell == nil)
{
- cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellType] autorelease];
+ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellType];
}
TodoItem *item = [[TodoList sharedInstance].items objectAtIndex:indexPath.row];
View
6 Examples/CryptoTodoList/CryptoTodoList.xcodeproj/project.pbxproj
@@ -263,6 +263,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -280,6 +281,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = CryptoTodoList_Prefix.pch;
@@ -296,8 +298,10 @@
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
+ RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
};
name = Debug;
@@ -308,8 +312,10 @@
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
+ RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
};
name = Release;
View
8 Examples/CryptoTodoList/main.m
@@ -10,8 +10,8 @@
int main(int argc, char *argv[]) {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- int retVal = UIApplicationMain(argc, argv, nil, nil);
- [pool release];
- return retVal;
+ @autoreleasepool {
+ int retVal = UIApplicationMain(argc, argv, nil, nil);
+ return retVal;
+ }
}
View
7 Examples/HRTodoList/Classes/NewItemViewController.m
@@ -13,7 +13,7 @@
@interface NewItemViewController()
-@property (nonatomic, retain) TodoItem *item;
+@property (nonatomic, strong) TodoItem *item;
@end
@@ -46,10 +46,5 @@ - (void)textViewDidChange:(UITextView *)textView
#pragma mark -
#pragma mark Cleanup
-- (void)dealloc
-{
- [item release];
- [super dealloc];
-}
@end
View
2  Examples/HRTodoList/Classes/TodoItem.h
@@ -13,7 +13,7 @@
@interface TodoItem : BaseModel
-@property (nonatomic, retain) NSString *label;
+@property (nonatomic, strong) NSString *label;
@property (nonatomic, assign) BOOL checked;
+ (instancetype)instanceWithLabel:(NSString *)label;
View
5 Examples/HRTodoList/Classes/TodoItem.m
@@ -40,11 +40,6 @@ - (void)save
[[TodoList sharedInstance] save];
}
-- (void)dealloc
-{
- [label release];
- [super dealloc];
-}
@end
View
2  Examples/HRTodoList/Classes/TodoList.h
@@ -13,6 +13,6 @@
@interface TodoList : BaseModel
-@property (nonatomic, retain) NSMutableArray *items;
+@property (nonatomic, strong) NSMutableArray *items;
@end
View
5 Examples/HRTodoList/Classes/TodoList.m
@@ -30,10 +30,5 @@ - (void)encodeWithCoder:(NSCoder *)aCoder
[aCoder encodeObject:items forKey:@"items"];
}
-- (void)dealloc
-{
- [items release];
- [super dealloc];
-}
@end
View
4 Examples/HRTodoList/Classes/TodoListAppDelegate.h
@@ -11,8 +11,8 @@
@interface TodoListAppDelegate : NSObject <UIApplicationDelegate>
-@property (nonatomic, retain) IBOutlet UIWindow *window;
-@property (nonatomic, retain) IBOutlet UIViewController *viewController;
+@property (nonatomic, strong) IBOutlet UIWindow *window;
+@property (nonatomic, strong) IBOutlet UIViewController *viewController;
@end
View
6 Examples/HRTodoList/Classes/TodoListAppDelegate.m
@@ -20,12 +20,6 @@ - (void)applicationDidFinishLaunching:(UIApplication *)application
[window makeKeyAndVisible];
}
-- (void)dealloc
-{
- [viewController release];
- [window release];
- [super dealloc];
-}
@end
View
3  Examples/HRTodoList/Classes/TodoListViewController.m
@@ -31,7 +31,6 @@ - (IBAction)createNewItem
{
UIViewController *viewController = [[NewItemViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
- [viewController release];
}
#pragma mark -
@@ -73,7 +72,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellType];
if (cell == nil)
{
- cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellType] autorelease];
+ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellType];
}
TodoItem *item = [[TodoList sharedInstance].items objectAtIndex:indexPath.row];
View
16 Examples/HRTodoList/HRCoder/HRCoder.h
@@ -1,7 +1,7 @@
//
// HRCoder.h
//
-// Version 1.1.3
+// Version 1.2
//
// Created by Nick Lockwood on 24/04/2012.
// Copyright (c) 2011 Charcoal Design
@@ -55,9 +55,13 @@ static NSString *const HRCoderObjectAliasKey = @"$alias";
NSMutableDictionary *_knownObjects;
NSMutableDictionary *_unresolvedAliases;
NSString *_keyPath;
+ NSMutableData *_data;
+ NSPropertyListFormat _outputFormat;
}
#endif
+@property (nonatomic, assign) NSPropertyListFormat outputFormat;
+
+ (id)unarchiveObjectWithPlist:(id)plist;
+ (id)unarchiveObjectWithData:(NSData *)data;
+ (id)unarchiveObjectWithFile:(NSString *)path;
@@ -65,11 +69,9 @@ static NSString *const HRCoderObjectAliasKey = @"$alias";
+ (NSData *)archivedDataWithRootObject:(id)rootObject;
+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;
-- (id)unarchiveObjectWithPlist:(id)plist;
-- (id)unarchiveObjectWithData:(NSData *)data;
-- (id)unarchiveObjectWithFile:(NSString *)path;
-- (id)archivedPlistWithRootObject:(id)rootObject;
-- (NSData *)archivedDataWithRootObject:(id)rootObject;
-- (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;
+- (id)initForReadingWithData:(NSData *)data;
+- (id)initForWritingWithMutableData:(NSMutableData *)data;
+- (void)finishDecoding;
+- (void)finishEncoding;
@end
View
238 Examples/HRTodoList/HRCoder/HRCoder.m
@@ -1,7 +1,7 @@
//
// HRCoder.m
//
-// Version 1.1.3
+// Version 1.2
//
// Created by Nick Lockwood on 24/04/2012.
// Copyright (c) 2011 Charcoal Design
@@ -33,7 +33,7 @@
#import "HRCoder.h"
-@interface NSObject (HRCoding)
+@interface NSObject (HRCoding_Private)
- (id)unarchiveObjectWithHRCoder:(HRCoder *)coder;
- (id)archivedObjectWithHRCoder:(HRCoder *)coder;
@@ -67,6 +67,7 @@ @interface HRCoder ()
@property (nonatomic, strong) NSMutableDictionary *knownObjects;
@property (nonatomic, strong) NSMutableDictionary *unresolvedAliases;
@property (nonatomic, strong) NSString *keyPath;
+@property (nonatomic, strong) NSMutableData *data;
+ (NSString *)classNameKey;
@@ -90,47 +91,33 @@ - (id)init
{
if ((self = [super init]))
{
- _stack = [[NSMutableArray alloc] initWithObjects:[NSMutableDictionary dictionary], nil];
+ self.stack = [NSMutableArray arrayWithObject:[NSMutableDictionary dictionary]];
_knownObjects = [[NSMutableDictionary alloc] init];
_unresolvedAliases = [[NSMutableDictionary alloc] init];
+ _outputFormat = NSPropertyListXMLFormat_v1_0;
}
return self;
}
-+ (id)unarchiveObjectWithPlist:(id)plist
-{
- HRCoder *coder = [[self alloc] init];
-
#if !__has_feature(objc_arc)
- [coder autorelease];
-#endif
-
- return [coder unarchiveObjectWithPlist:plist];
-}
-+ (id)unarchiveObjectWithData:(NSData *)data
+- (void)dealloc
{
- HRCoder *coder = [[self alloc] init];
-
-#if !__has_feature(objc_arc)
- [coder autorelease];
-#endif
-
- return [coder unarchiveObjectWithData:data];
+ [_stack release];
+ [_knownObjects release];
+ [_unresolvedAliases release];
+ [_keyPath release];
+ [_data release];
+ [super dealloc];
}
-+ (id)unarchiveObjectWithFile:(NSString *)path
-{
- HRCoder *coder = [[self alloc] init];
-
-#if !__has_feature(objc_arc)
- [coder autorelease];
#endif
-
- return [coder unarchiveObjectWithFile:path];
-}
-+ (id)archivedPlistWithRootObject:(id)rootObject
+
+#pragma mark -
+#pragma mark Unarchiving
+
++ (id)unarchiveObjectWithPlist:(id)plist
{
HRCoder *coder = [[self alloc] init];
@@ -138,32 +125,31 @@ + (id)archivedPlistWithRootObject:(id)rootObject
[coder autorelease];
#endif
- return [coder archivedPlistWithRootObject:rootObject];
+ return [coder unarchiveRootObjectWithPlist:plist];
}
-+ (NSData *)archivedDataWithRootObject:(id)rootObject
++ (id)unarchiveObjectWithData:(NSData *)data
{
- HRCoder *coder = [[self alloc] init];
-
-#if !__has_feature(objc_arc)
- [coder autorelease];
-#endif
+ //attempt to deserialise data as a plist
+ id plist = nil;
+ if (data)
+ {
+ NSPropertyListFormat format;
+ NSPropertyListReadOptions options = NSPropertyListMutableContainersAndLeaves;
+ plist = [NSPropertyListSerialization propertyListWithData:data options:options format:&format error:NULL];
+ }
- return [coder archivedDataWithRootObject:rootObject];
+ //unarchive
+ return [self unarchiveObjectWithPlist:plist];
}
-+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path
++ (id)unarchiveObjectWithFile:(NSString *)path
{
- HRCoder *coder = [[self alloc] init];
-
-#if !__has_feature(objc_arc)
- [coder autorelease];
-#endif
-
- return [coder archiveRootObject:rootObject toFile:path];
+ //load the file
+ return [self unarchiveObjectWithData:[NSData dataWithContentsOfFile:path]];
}
-- (id)unarchiveObjectWithPlist:(id)plist
+- (id)unarchiveRootObjectWithPlist:(id)plist
{
[_stack removeAllObjects];
[_knownObjects removeAllObjects];
@@ -203,68 +189,115 @@ - (id)unarchiveObjectWithPlist:(id)plist
}
}
}
- [_unresolvedAliases removeAllObjects];
- [_knownObjects removeAllObjects];
- [_stack removeAllObjects];
+ [self finishDecoding];
return rootObject;
}
-- (id)unarchiveObjectWithData:(NSData *)data
+- (id)initForReadingWithData:(NSData *)data
{
- //attempt to deserialise data as a plist
- id plist = nil;
- if (data)
+ if ((self = [self init]))
{
- NSPropertyListFormat format;
- NSPropertyListReadOptions options = NSPropertyListMutableContainersAndLeaves;
- plist = [NSPropertyListSerialization propertyListWithData:data options:options format:&format error:NULL];
+ //attempt to deserialise data as a plist
+ if (data)
+ {
+ //load plist
+ NSPropertyListReadOptions options = NSPropertyListMutableContainersAndLeaves;
+ id plist = [NSPropertyListSerialization propertyListWithData:data
+ options:options
+ format:&_outputFormat
+ error:NULL];
+ //only works if root object is a dictionary
+ if ([plist isKindOfClass:[NSDictionary class]])
+ {
+ [_stack addObject:plist];
+ }
+ else
+ {
+ [NSException raise:NSGenericException format:@"initForReadingWithData: method requires that the root object in the plist data is an NSDictionary. Decoding an %@ is not supported with this method. Try using unarchiveObjectWithData: instead.", [plist class]];
+ }
+ }
}
-
- //unarchive
- return [self unarchiveObjectWithPlist:plist];
+ return self;
}
-- (id)unarchiveObjectWithFile:(NSString *)path
+- (void)finishDecoding
{
- //load the file
- return [self unarchiveObjectWithData:[NSData dataWithContentsOfFile:path]];
+ [_unresolvedAliases removeAllObjects];
+ [_knownObjects removeAllObjects];
+ [_stack setArray:[NSMutableArray arrayWithObject:[NSMutableDictionary dictionary]]];
}
-- (id)archivedPlistWithRootObject:(id)rootObject
+
+#pragma mark -
+#pragma mark Archiving
+
++ (id)archivedPlistWithRootObject:(id)rootObject
{
- [_stack removeAllObjects];
- [_knownObjects removeAllObjects];
- if (rootObject) [_knownObjects setObject:rootObject forKey:HRCoderRootObjectKey];
- id plist = [rootObject archivedObjectWithHRCoder:self];
- [_knownObjects removeAllObjects];
- [_stack removeAllObjects];
+ HRCoder *coder = [[self alloc] init];
+ id plist = [coder archiveRootObject:rootObject];
+
+#if !__has_feature(objc_arc)
+ [coder release];
+#endif
+
return plist;
}
-- (NSData *)archivedDataWithRootObject:(id)rootObject
++ (NSData *)archivedDataWithRootObject:(id)rootObject
{
- id object = [self archivedPlistWithRootObject:rootObject];
- NSPropertyListFormat format = NSPropertyListXMLFormat_v1_0;
- return [NSPropertyListSerialization dataWithPropertyList:object format:format options:0 error:NULL];
+ NSMutableData *data = [NSMutableData data];
+ HRCoder *coder = [[self alloc] initForWritingWithMutableData:data];
+ [coder archiveRootObject:rootObject];
+
+#if !__has_feature(objc_arc)
+ [coder release];
+#endif
+
+ return data;
}
-- (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path
++ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path
{
return [[self archivedDataWithRootObject:rootObject] writeToFile:path atomically:YES];
}
-#if !__has_feature(objc_arc)
+- (id)initForWritingWithMutableData:(NSMutableData *)data
+{
+ if ((self = [self init]))
+ {
+ //set data
+ self.data = data;
+ }
+ return self;
+}
-- (void)dealloc
+- (id)archiveRootObject:(id)rootObject
{
- [_stack release];
- [_knownObjects release];
- [_unresolvedAliases release];
- [_keyPath release];
- [super dealloc];
+ [_knownObjects removeAllObjects];
+ [self encodeRootObject:rootObject];
+ id plist = [_stack lastObject];
+ [self finishEncoding];
+ return plist;
+}
+
+- (void)finishEncoding
+{
+ if (_data)
+ {
+ NSData *data = [NSPropertyListSerialization dataWithPropertyList:[_stack lastObject]
+ format:_outputFormat
+ options:0
+ error:NULL];
+ [_data setData:data];
+ self.data = nil;
+ }
+ [_knownObjects removeAllObjects];
+ [_stack setArray:[NSMutableArray arrayWithObject:[NSMutableDictionary dictionary]]];
}
-#endif
+
+#pragma mark -
+#pragma mark NSCoding
- (BOOL)allowsKeyedCoding
{
@@ -308,6 +341,15 @@ - (void)encodeObject:(id)objv forKey:(NSString *)key
if (object) [[_stack lastObject] setObject:object forKey:key];
}
+- (void)encodeRootObject:(id)rootObject
+{
+ if (rootObject)
+ {
+ [_knownObjects setObject:rootObject forKey:HRCoderRootObjectKey];
+ [_stack setArray:[NSMutableArray arrayWithObject:[rootObject archivedObjectWithHRCoder:self]]];
+ }
+}
+
- (void)encodeConditionalObject:(id)objv forKey:(NSString *)key
{
if ([[_knownObjects allValues] containsObject:objv])
@@ -433,14 +475,10 @@ - (const uint8_t *)decodeBytesForKey:(NSString *)key returnedLength:(NSUInteger
@implementation NSObject(HRCoding)
-- (BOOL)useHRCoderIfAvailable
-{
- return YES;
-}
-
- (id)unarchiveObjectWithHRCoder:(HRCoder *)coder
{
- return self;
+ [NSException raise:NSGenericException format:@"%@ is not a supported HRCoder archive type", [self class]];
+ return nil;
}
- (id)archivedObjectWithHRCoder:(HRCoder *)coder
@@ -540,7 +578,12 @@ - (id)archivedObjectWithHRCoder:(HRCoder *)coder
@implementation NSString(HRCoding)
-
+
+- (id)unarchiveObjectWithHRCoder:(HRCoder *)coder
+{
+ return self;
+}
+
- (id)archivedObjectWithHRCoder:(HRCoder *)coder
{
return self;
@@ -551,6 +594,11 @@ - (id)archivedObjectWithHRCoder:(HRCoder *)coder
@implementation NSData(HRCoding)
+- (id)unarchiveObjectWithHRCoder:(HRCoder *)coder
+{
+ return self;
+}
+
- (id)archivedObjectWithHRCoder:(HRCoder *)coder
{
return self;
@@ -561,6 +609,11 @@ - (id)archivedObjectWithHRCoder:(HRCoder *)coder
@implementation NSNumber(HRCoding)
+- (id)unarchiveObjectWithHRCoder:(HRCoder *)coder
+{
+ return self;
+}
+
- (id)archivedObjectWithHRCoder:(HRCoder *)coder
{
return self;
@@ -571,6 +624,11 @@ - (id)archivedObjectWithHRCoder:(HRCoder *)coder
@implementation NSDate(HRCoding)
+- (id)unarchiveObjectWithHRCoder:(HRCoder *)coder
+{
+ return self;
+}
+
- (id)archivedObjectWithHRCoder:(HRCoder *)coder
{
return self;
View
8 Examples/HRTodoList/HRTodoList.xcodeproj/project.pbxproj
@@ -200,7 +200,7 @@
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0440;
+ LastUpgradeCheck = 0450;
};
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "HRTodoList" */;
compatibilityVersion = "Xcode 3.2";
@@ -259,6 +259,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -275,6 +276,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = HRTodoList_Prefix.pch;
@@ -290,8 +292,10 @@
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
+ RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
};
name = Debug;
@@ -302,8 +306,10 @@
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
+ RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
};
name = Release;
View
1  Examples/HRTodoList/HRTodoList.xcodeproj/xcshareddata/xcschemes/HRTodoList.xcscheme
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
+ LastUpgradeVersion = "0450"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
View
8 Examples/HRTodoList/main.m
@@ -10,8 +10,8 @@
int main(int argc, char *argv[]) {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- int retVal = UIApplicationMain(argc, argv, nil, nil);
- [pool release];
- return retVal;
+ @autoreleasepool {
+ int retVal = UIApplicationMain(argc, argv, nil, nil);
+ return retVal;
+ }
}
View
7 Examples/TodoList/Classes/NewItemViewController.m
@@ -13,7 +13,7 @@
@interface NewItemViewController()
-@property (nonatomic, retain) TodoItem *item;
+@property (nonatomic, strong) TodoItem *item;
@end
@@ -46,10 +46,5 @@ - (void)textViewDidChange:(UITextView *)textView
#pragma mark -
#pragma mark Cleanup
-- (void)dealloc
-{
- [item release];
- [super dealloc];
-}
@end
View
2  Examples/TodoList/Classes/TodoItem.h
@@ -13,7 +13,7 @@
@interface TodoItem : BaseModel
-@property (nonatomic, retain) NSString *label;
+@property (nonatomic, strong) NSString *label;
@property (nonatomic, assign) BOOL checked;
+ (instancetype)instanceWithLabel:(NSString *)label;
View
5 Examples/TodoList/Classes/TodoItem.m
@@ -43,11 +43,6 @@ - (void)save
[[TodoList sharedInstance] save];
}
-- (void)dealloc
-{
- [label release];
- [super dealloc];
-}
@end
View
2  Examples/TodoList/Classes/TodoList.h
@@ -13,6 +13,6 @@
@interface TodoList : BaseModel
-@property (nonatomic, retain) NSMutableArray *items;
+@property (nonatomic, strong) NSMutableArray *items;
@end
View
5 Examples/TodoList/Classes/TodoList.m
@@ -36,10 +36,5 @@ - (void)encodeWithCoder:(NSCoder *)aCoder
[aCoder encodeObject:items forKey:@"items"];
}
-- (void)dealloc
-{
- [items release];
- [super dealloc];
-}
@end
View
4 Examples/TodoList/Classes/TodoListAppDelegate.h
@@ -11,8 +11,8 @@
@interface TodoListAppDelegate : NSObject <UIApplicationDelegate>
-@property (nonatomic, retain) IBOutlet UIWindow *window;
-@property (nonatomic, retain) IBOutlet UIViewController *viewController;
+@property (nonatomic, strong) IBOutlet UIWindow *window;
+@property (nonatomic, strong) IBOutlet UIViewController *viewController;
@end
View
6 Examples/TodoList/Classes/TodoListAppDelegate.m
@@ -19,12 +19,6 @@ - (void)applicationDidFinishLaunching:(UIApplication *)application
[window makeKeyAndVisible];
}
-- (void)dealloc
-{
- [viewController release];
- [window release];
- [super dealloc];
-}
@end
View
3  Examples/TodoList/Classes/TodoListViewController.m
@@ -31,7 +31,6 @@ - (IBAction)createNewItem
{
UIViewController *viewController = [[NewItemViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
- [viewController release];
}
#pragma mark -
@@ -73,7 +72,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellType];
if (cell == nil)
{
- cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellType] autorelease];
+ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellType];
}
TodoItem *item = [[TodoList sharedInstance].items objectAtIndex:indexPath.row];
View
6 Examples/TodoList/TodoList.xcodeproj/project.pbxproj
@@ -245,6 +245,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -262,6 +263,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = TodoList_Prefix.pch;
@@ -278,8 +280,10 @@
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
+ RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
};
name = Debug;
@@ -290,8 +294,10 @@
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
+ RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
};
name = Release;
View
8 Examples/TodoList/main.m
@@ -10,8 +10,8 @@
int main(int argc, char *argv[]) {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- int retVal = UIApplicationMain(argc, argv, nil, nil);
- [pool release];
- return retVal;
+ @autoreleasepool {
+ int retVal = UIApplicationMain(argc, argv, nil, nil);
+ return retVal;
+ }
}
View
2  LICENCE.md
@@ -1,6 +1,6 @@
BaseModel
-version 2.3.5, October 29th, 2012
+version 2.4, November 25th, 2012
Copyright (C) 2011 Charcoal Design
View
65 README.md
@@ -3,23 +3,23 @@ Purpose
BaseModel provides a base class for building model objects for your iOS or Mac OS projects. It saves you the hassle of writing boilerplate code, and encourages good practices by reducing the incentive to cut corners in your model implementation.
-The BaseModel object uses Plists and the NSCoding protocol for serialisation. It is not designed for use with Core Data, although in principle the class could be extended to work with Core Data if needed by changing the BaseModel superclass to an NSManagedObject.
+The BaseModel object uses property lists and the NSCoding protocol for serialisation. It is not designed for use with Core Data, although in principle the class could be extended to work with Core Data if needed by changing the BaseModel superclass to an NSManagedObject.
BaseModel is really designed as an *alternative* to Core Data for developers who prefer to have a little more control over the implementation of their data stack. BaseModel gives you precise control over the location and serialisation of your data files, whilst still proving enough automatic behaviour to save you from writing the same code over and over again.
-BaseModel is designed to work with the AutoCoding library (https://github.com/nicklockwood/AutoCoding). It does not require this library to function, but when used in combination, BaseModel objects support automatic loading and saving without needing to write your own NSCoding methods. Use of AutoCoding is completely optional. For an example of how it works, check out the *AutoTodoList* example.
+BaseModel is designed to work with the AutoCoding library (https://github.com/nicklockwood/AutoCoding). It does not require this library to function but when used in combination, BaseModel objects support automatic loading and saving without needing to write your own NSCoding methods. Use of AutoCoding is completely optional. For an example of how it works, check out the *AutoTodoList* example.
-BaseModel is also designed to work with the HRCoder library (https://github.com/nicklockwood/HRCoder) as an alternative mechanism for loading an saving data in a human readable/editable format. When used in conjunction with AutoCoding, HRCoder allows you to specify your data files in a standard format and avoid having to write any `setWith...` methods. Use of HRCoder is completely optional. For an example of how this works, check out the *HRTodoList* example.
+BaseModel is also designed to work with the HRCoder library (https://github.com/nicklockwood/HRCoder) as an alternative mechanism for loading and saving data in a human readable/editable format. HRCoder allows you to specify your data files in a standard format, and when used in conjunction with AutoCoding avoids the need for you to write any `setWith...` methods. Use of HRCoder is completely optional. For an example of how this works, check out the *HRTodoList* example.
BaseModel is also designed to work with the CryptoCoding library (https://github.com/nicklockwood/CryptoCoding). It does not require this library to function, but when used in conjunction with CryptoCoding, BaseModel objects support automatic AES encryption of the entire object when saved or loaded to disk. Use of CryptoCoding is completely optional. For an example of how it works, check out the *CryptoTodoList* example.
-**Note:** You can combine AutoCoding with either HRCoder or CryptoCoding. AutoCoding is independent of the coding scheme used.
+**Note:** You can combine AutoCoding with either HRCoder or CryptoCoding, or neither. AutoCoding is independent of the coding scheme used.
Supported OS & SDK Versions
-----------------------------
-* Supported build target - iOS 6.0 / Mac OS 10.8 (Xcode 4.5.1, Apple LLVM compiler 4.1)
+* Supported build target - iOS 6.0 / Mac OS 10.8 (Xcode 4.5.2, Apple LLVM compiler 4.1)
* Earliest supported deployment target - iOS 5.0 / Mac OS 10.7
* Earliest compatible deployment target - iOS 4.3 / Mac OS 10.6
@@ -29,19 +29,21 @@ NOTE: 'Supported' means that the library has been tested with this version. 'Com
ARC Compatibility
------------------
-As of version 1.2.1, BaseModel automatically works with both ARC and non-ARC projects through conditional compilation. There is no need to exclude BaseModel files from the ARC validation process, or to convert BaseModel using the ARC conversion tool.
+As of version 2.4, BaseModel requires ARC. If you wish to use BaseModel in a non-ARC project, just add the -fobjc-arc compiler flag to the BaseModel.m class. To do this, go to the Build Phases tab in your target settings, open the Compile Sources group, double-click BaseModel.m in the list and type -fobjc-arc into the popover.
+
+If you wish to convert your whole project to ARC, comment out the #error line in BaseModel.m, then run the Edit > Refactor > Convert to Objective-C ARC... tool in Xcode and make sure all files that you wish to use ARC for (including BaseModel.m) are checked.
Thread Safety
--------------
-BaseModel instances can only be safely created on the main thread. Once created, it should be safe to access them from multiple threads, provided that any custom methods and properties that are added by the user are thread-safe.
+BaseModel instances can be safely created on any thread, however a number of BaseModel operations are synchronized on a per-class basis, so creating BaseModel instances or accessing the shared instance concurrently on multiple threads may lead to unexpected performance issues.
Installation
--------------
-To use the BaseModel class in your project, just drag the BaseModel.h and .m files into your project. BaseModel has no required dependencies, however you may wish to also include the optional AutoCoding library (https://github.com/nicklockwood/AutoCoding) to get the full benefits of using BaseModel, and you may also wish to include the HRCoding (https://github.com/nicklockwood/HRCoding) and CryptoCoding (https://github.com/nicklockwood/CryptoCoding) libraries to enable additional BaseModel functionality.
+To use the BaseModel class in your project, just drag the BaseModel.h and .m files into your project. BaseModel has no required dependencies, however you may wish to also include the optional AutoCoding library (https://github.com/nicklockwood/AutoCoding) to get the full benefits of using BaseModel, and you may also wish to include the HRCoding (https://github.com/nicklockwood/HRCoding) or CryptoCoding (https://github.com/nicklockwood/CryptoCoding) libraries to enable additional BaseModel functionality.
Classes
@@ -57,14 +59,12 @@ The BaseModel class implements the BaseModel protocol, which specifies some opti
- (void)setUp;
-BaseModel's initialisation routine is quite complex, and it is not advised that you attempt to override any of the constructor methods (there is no 'designated initializer'). Instead, to perform initialisation logic, implement the setUp method. This is called after the class has been successfully initialised, so there is no need to verify that self is not nil or call `[super setUp]` (although it is safe to do so). Like `init`, `setUp` is called only once at the start of the lifetime of an instance, so it is safe to set properties without releasing their existing values. You should never call `setUp` yourself directly, except in the context of calling `[super setUp]` when subclassing your own BaseModel subclasses.
+BaseModel's initialisation routine is quite complex and follows a multi-step process. To simplify configuration, BaseModel provides a `setUp` method that is called before anything else so you can pre-configure your instance. This is called only after a successful `[super init]`, so there is no need to verify that self is not nil or call `[super setUp]` (unless you are subclassing one of your own BaseModel subclasses). Like `init`, `setUp` is called only once at the start of the lifetime of an instance, so it is safe to set properties without releasing their existing values (relevant only if you are not using ARC). You should never call `setUp` yourself directly, except in the context of calling `[super setUp]` when subclassing your own BaseModel subclasses.
- (void)setWithDictionary:(NSDictionary *)dict;
- (void)setWithArray:(NSArray *)array;
- (void)setWithString:(NSString *)string;
- - (void)setWithNumber:(NSNumber *)number;
- - (void)setWithData:(NSData *)data;
-
+
These methods are used to extend you model with the capability to be initialised from a standard object type (i.e. one that is supported by the Plist format). This is useful as it allows objects to be automatically loaded from a Plist in your application bundle. It could also be used to instantiate objects from other formats like JSON or XML.
These methods are called after the `setUp` method, so you should not assume that class properties and ivars do not already have values at the point when this method is called. If you are not using ARC, be careful to safely release any property values before setting them. **Note:** these methods are defined in the protocol only to help with code autocompletion - it is actually possible to initialise a BaseModel instance with any kind of object if you define an appropriate setup method of the form `setWith[ClassName]:` in your BaseModel subclass.
@@ -80,16 +80,6 @@ This method is called by `initWithCoder:` as part of the NSCoding implementation
The BaseModel class can optionally implement the NSCoding protocol methods. If you are implementing NSCoding serialisation for your class, you should implement this method. Note that if you are using the AutoCoding library, this method is already implemented for you.
-Optional properties
---------------
-
-The BaseModel class has the following instance property, which is disabled by default (to allow developer complete control over their model structure) but can be enabled by adding BASEMODEL_ENABLE_UNIQUE_ID to your project's preprocessor macros, which can be found in the build settings:
-
- @property (nonatomic, retain) NSString *uniqueID;
-
-This property is used to store a unique identifier for a given BaseModel instance. The uniqueID getter method is actually a lazy constructor that uses the `newUniqueIdentifier` method to create a globally unique ID the first time it is called for each model instance, so you should normally not need to set this property unless you need to override the ID for a specific object. Note however that responsibility for loading, saving and copying this ID is left to the developer. If you do not preserve this value when saving and loading your model then it will be different each time the object is re-instantiated (unless you are using the AutoCoding library in which case this property is automatically saved and loaded along with the other class properties).
-
-
Methods
---------------
@@ -103,11 +93,11 @@ Create an autoreleased instance or initialises a retained instance of the class
+ (instancetype)instanceWithObject:(NSDictionary *)dict;
- (instancetype)initWithObject:(NSDictionary *)dict;
-Creates an instance of the class and initialises it using the supplied object. This is useful when loading a model from an embedded Plist file in the application bundle, or creating model objects from JSON data returned by a web service. This method requires that an appropriate setter method is defined on your class, where the setter name is of the form `setWith[ClassName]:`. For example, to initialise the model using an NSDictionary, your BaseModel subclass must have a method called `setWithDictionary:`. This method will attempt to initialise the class from `resourceFile` (if that file exists) prior to calling `setWith[ClassName]:`, so if your resourceFile contains a serialised object, this method will be called twice (with different input).
+Creates an instance of the class and initialises it using the supplied object. This is useful when loading a model from an embedded property list file in the application bundle, or creating model objects from JSON data returned by a web service. This method requires that an appropriate setter method is defined on your class, where the setter name is of the form `setWith[ClassName]:`. For example, to initialise the model using an NSDictionary, your BaseModel subclass must have a method called `setWithDictionary:`. This method will attempt to initialise the class from `resourceFile` (if that file exists) prior to calling `setWith[ClassName]:`, so if your resourceFile contains a serialised object, this method will be called twice (with different input).
+ (NSArray *)instancesWithArray:(NSArray *)array;
-This is similar to `instanceWithArray:` but instead of initialising the object with an array, it iterates over each item in the array and creates an instance from each object by calling `instanceWithArray:` or `instanceWithDictionary:`, depending on the object type. The resultant objects are then returned as a new array. It is expected that the objects in the array will be either NSDictionary or NSArray instances. Any other object type will be returned unmodified.
+This is similar to calling `instanceWithObject:` with an array parameter, but instead of initialising the object with an array, it iterates over each item in the array and creates an instance from each object by calling `instanceWithObject:` for each array element. The resultant BaseModel instances are then returned as a new array.
+ (instancetype)instanceWithCoder:(NSCoder *)decoder;
- (instancetype)initWithCoder:(NSCoder *)decoder;
@@ -117,7 +107,7 @@ Initialises the object from an NSCoded archive using the NSCoding protocol. This
+ (instancetype)instanceWithContentsOfFile:(NSString *)filePath;
- (instancetype)initWithContentsOfFile:(NSString *)filePath;
-Creates/initialises an instance of the model class from a file. If the file is a Plist then the model will be initialised using the appropriate `setWith[ClassName]:` method, depending on the type of the root object in the Plist. If the file is an NSCoded archive of an instance of the model then the object will be initialised using `setWithCoder:`. If the file format is not recognised, or the appropriate setWith... function is not implemented, the method will return nil.
+Creates/initialises an instance of the model class from a file. If the file is a property list then the model will be initialised using the appropriate `setWith[ClassName]:` method, depending on the type of the root object in the Plist. If the file is an NSCoded archive of an instance of the model then the object will be initialised using `setWithCoder:`. If the file format is not recognised, or the appropriate setWith... function is not implemented, the method will return nil.
+ (instancetype)sharedInstance;
@@ -129,7 +119,7 @@ Because `sharedInstance` is a lazy constructor, you cannot call it to test if a
+ (void)setSharedInstance:(BaseModel *)instance;
-This method lets you replace the current shared instance with another instance of the model. This is useful for mutable models that may be re-loaded from a file or downloaded from a web service. The previous shared instance will be autoreleased when the new one is set. Note that since the shared model instance is replaced, any objects hanging on to references to the model will need to be updated. The model broadcasts a `BaseModelSharedInstanceUpdatedNotification` via NSNotificationCenter whenever this method is called, so setting an observer for that event is a good way to make sure that any retained references are updated. You can also set the BaseModel shared instance to nil in order to reclaim memory for BaseModel shared instances if they are no longer needed, or in the event of a memory warning.
+This method lets you replace the current shared instance with another instance of the model. This is useful for models that may be re-loaded from a file or downloaded from a web service. The previous shared instance will be autoreleased when the new one is set. Note that since the shared model instance is replaced, any objects hanging on to references to the model will need to be updated. The model broadcasts a `BaseModelSharedInstanceUpdatedNotification` via NSNotificationCenter whenever this method is called, so setting an observer for that event is a good way to make sure that any retained references are updated. You can also set the BaseModel shared instance to nil in order to reclaim memory for BaseModel shared instances if they are no longer needed, or in the event of a memory warning.
+ (void)reloadSharedInstance;
@@ -141,11 +131,15 @@ This method attempts to serialise the model object to the specified file using N
- (BOOL)useHRCoderIfAvailable;
-Normally, BaseModel saves files using NSCoding and the NSKeyedArchiver, which creates binary Plist files in a complex format that is not human-readable or editable. The HRCoder library provides an alternative format for NSCoding that is more similar to an ordinary, hand-written Plist file. BaseModel detects the presence of the HRCoder class if it's included in the project and will use it by default if available. If you do not want to use HRCoder for saving a particular model, override this method and return NO. BaseModel is still able to load archives that are encoded using the HRCoding protocol if this setting is set to NO, but if you save over the file it will use NSKeyedArchiver.
+Normally, BaseModel saves files using NSCoding and the NSKeyedArchiver, which creates binary property list files in a complex format that is not human-readable or editable. The HRCoder library provides an alternative format for NSCoding that is more similar to an ordinary, hand-written property list file. BaseModel detects the presence of the HRCoder class if it's included in the project and will use it by default if available. If you do not want to use HRCoder for saving a particular model, override this method and return NO. BaseModel is still able to load archives that are encoded using the HRCoding protocol if this method returns NO, but if you save over the original file it will use NSKeyedArchiver.
+
+ + (NSString *)newUniqueIdentifier;
+
+This method is used to generate a new, globally unique identifier. Each time it is called it uses the CFUUID APIs to create a brand new value that will never be repeated on any device. You can store this value in a property in your model to give it a unique identifier suitable for maintaining references to the object when it is loaded and saved. Note however that responsibility for loading, saving and copying this ID is left to the developer. If you do not preserve this value when saving and loading your model then it will be different each time the object is re-instantiated.