Compare commits
12 Commits
master
...
signal-mas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7e46253bb | ||
|
|
b72c2d1e61 | ||
|
|
0ad65248f1 | ||
|
|
9599b1d979 | ||
|
|
cd39677318 | ||
|
|
97f94a0ea4 | ||
|
|
86be628090 | ||
|
|
59b5685fd5 | ||
|
|
876c193270 | ||
|
|
edf68e32c7 | ||
|
|
4a60daec18 | ||
|
|
5902172226 |
30
Mantle.podspec.json
Normal file
30
Mantle.podspec.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "Mantle",
|
||||
"version": "2.1.0",
|
||||
"summary": "Model framework for Cocoa and Cocoa Touch.",
|
||||
"homepage": "https://github.com/Mantle/Mantle",
|
||||
"license": "MIT",
|
||||
"authors": {
|
||||
"Github": "support@github.com"
|
||||
},
|
||||
"source": {
|
||||
"git": "https://github.com/github/Mantle.git",
|
||||
"tag": "2.1.0"
|
||||
},
|
||||
"platforms": {
|
||||
"ios": "5.0",
|
||||
"osx": "10.7",
|
||||
"watchos": "2.0",
|
||||
"tvos": "9.0"
|
||||
},
|
||||
"requires_arc": true,
|
||||
"frameworks": "Foundation",
|
||||
"source_files": "Mantle",
|
||||
"subspecs": [
|
||||
{
|
||||
"name": "extobjc",
|
||||
"source_files": "Mantle/extobjc",
|
||||
"private_header_files": "Mantle/extobjc/*.h"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -359,7 +359,18 @@ NSString * const MTLJSONAdapterThrownExceptionErrorKey = @"MTLJSONAdapterThrownE
|
||||
|
||||
id model = [self.modelClass modelWithDictionary:dictionaryValue error:error];
|
||||
|
||||
return [model validate:error] ? model : nil;
|
||||
// BEGIN ORM-PERF-2
|
||||
// Commented out by mkirk as part of ORM perf optimizations.
|
||||
//
|
||||
// The validation NSCoding validation reflection used by Mantle is expensive, and
|
||||
// we've never used it.
|
||||
// If we later want to use this feature, we'll need to carefully evaluate the perf
|
||||
// implications on large migrations.
|
||||
//
|
||||
// return [model validate:error] ? model : nil;
|
||||
//
|
||||
return model;
|
||||
// END ORM-PERF-2
|
||||
}
|
||||
|
||||
+ (NSDictionary *)valueTransformersForModelClass:(Class)modelClass {
|
||||
|
||||
@ -17,6 +17,15 @@ static NSString * const MTLModelVersionKey = @"MTLModelVersion";
|
||||
// Used to cache the reflection performed in +allowedSecureCodingClassesByPropertyKey.
|
||||
static void *MTLModelCachedAllowedClassesKey = &MTLModelCachedAllowedClassesKey;
|
||||
|
||||
// BEGIN ORM-PERF-3
|
||||
// Added by mkirk as part of ORM perf optimizations.
|
||||
//
|
||||
// +encodingBehaviorsByPropertyKey is somewhat expensive, so we follow existing library patterns
|
||||
// to cache the computed reflection on the class via an associated object
|
||||
// Used to cache the reflection performed in +encodingBehaviorsByPropertyKey.
|
||||
static void *MTLModelCachedEncodingBehaviorsByPropertyKeyKey = &MTLModelCachedEncodingBehaviorsByPropertyKeyKey;
|
||||
// END ORM-PERF-3
|
||||
|
||||
// Returns whether the given NSCoder requires secure coding.
|
||||
static BOOL coderRequiresSecureCoding(NSCoder *coder) {
|
||||
SEL requiresSecureCodingSelector = @selector(requiresSecureCoding);
|
||||
@ -63,6 +72,15 @@ static void verifyAllowedClassesByPropertyKey(Class modelClass) {
|
||||
#pragma mark Encoding Behaviors
|
||||
|
||||
+ (NSDictionary *)encodingBehaviorsByPropertyKey {
|
||||
// BEGIN ORM-PERF-3
|
||||
// Added by mkirk as part of ORM perf optimizations.
|
||||
//
|
||||
// +encodingBehaviorsByPropertyKey is somewhat expensive, so we follow existing library patterns
|
||||
// to cache the computed reflection on the class via an associated object
|
||||
NSDictionary *cachedBehaviors = objc_getAssociatedObject(self, MTLModelCachedEncodingBehaviorsByPropertyKeyKey);
|
||||
if (cachedBehaviors != nil) return cachedBehaviors;
|
||||
// END ORM-PERF-3
|
||||
|
||||
NSSet *propertyKeys = self.propertyKeys;
|
||||
NSMutableDictionary *behaviors = [[NSMutableDictionary alloc] initWithCapacity:propertyKeys.count];
|
||||
|
||||
@ -79,6 +97,14 @@ static void verifyAllowedClassesByPropertyKey(Class modelClass) {
|
||||
behaviors[key] = @(behavior);
|
||||
}
|
||||
|
||||
// BEGIN ORM-PERF-3
|
||||
// Added by mkirk as part of ORM perf optimizations.
|
||||
//
|
||||
// +encodingBehaviorsByPropertyKey is somewhat expensive, so we follow existing library patterns
|
||||
// to cache the computed reflection on the class via an associated object
|
||||
objc_setAssociatedObject(self, MTLModelCachedEncodingBehaviorsByPropertyKeyKey, behaviors, OBJC_ASSOCIATION_COPY);
|
||||
// END ORM-PERF-3
|
||||
|
||||
return behaviors;
|
||||
}
|
||||
|
||||
@ -126,14 +152,21 @@ static void verifyAllowedClassesByPropertyKey(Class modelClass) {
|
||||
NSParameterAssert(key != nil);
|
||||
NSParameterAssert(coder != nil);
|
||||
|
||||
SEL selector = MTLSelectorWithCapitalizedKeyPattern("decode", key, "WithCoder:modelVersion:");
|
||||
if ([self respondsToSelector:selector]) {
|
||||
IMP imp = [self methodForSelector:selector];
|
||||
id (*function)(id, SEL, NSCoder *, NSUInteger) = (__typeof__(function))imp;
|
||||
id result = function(self, selector, coder, modelVersion);
|
||||
|
||||
return result;
|
||||
}
|
||||
// BEGIN ORM-PERF-1
|
||||
// Commented out by mkirk as part of ORM perf optimizations.
|
||||
// The `MTLSelectorWithCapitalizedKeyPattern` can be quite expensive in aggregate
|
||||
// and we're not using the reflective features that require it.
|
||||
// If we later want to use this feature, we'll need to carefully evaluate the perf
|
||||
// implications on large migrations.
|
||||
// SEL selector = MTLSelectorWithCapitalizedKeyPattern("decode", key, "WithCoder:modelVersion:");
|
||||
// if ([self respondsToSelector:selector]) {
|
||||
// IMP imp = [self methodForSelector:selector];
|
||||
// id (*function)(id, SEL, NSCoder *, NSUInteger) = (__typeof__(function))imp;
|
||||
// id result = function(self, selector, coder, modelVersion);
|
||||
//
|
||||
// return result;
|
||||
// }
|
||||
// END ORM-PERF-1
|
||||
|
||||
@try {
|
||||
if (coderRequiresSecureCoding(coder)) {
|
||||
|
||||
@ -48,7 +48,7 @@ typedef enum : NSUInteger {
|
||||
/// (like a KVC validation error).
|
||||
///
|
||||
/// Returns an initialized model object, or nil if validation failed.
|
||||
+ (instancetype)modelWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error;
|
||||
+ (instancetype)modelWithDictionary:(NSDictionary<NSString *, id> *)dictionaryValue error:(NSError **)error;
|
||||
|
||||
/// A dictionary representing the properties of the receiver.
|
||||
///
|
||||
@ -56,7 +56,7 @@ typedef enum : NSUInteger {
|
||||
/// with any nil values represented by NSNull.
|
||||
///
|
||||
/// This property must never be nil.
|
||||
@property (nonatomic, copy, readonly) NSDictionary *dictionaryValue;
|
||||
@property (nonatomic, copy, readonly) NSDictionary<NSString *, id> *dictionaryValue;
|
||||
|
||||
/// Initializes the receiver using key-value coding, setting the keys and values
|
||||
/// in the given dictionary.
|
||||
@ -74,15 +74,22 @@ typedef enum : NSUInteger {
|
||||
/// (like a KVC validation error).
|
||||
///
|
||||
/// Returns an initialized model object, or nil if validation failed.
|
||||
- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error;
|
||||
- (instancetype)initWithDictionary:(NSDictionary<NSString *, id> *)dictionaryValue error:(NSError **)error;
|
||||
|
||||
// BEGIN ORM-PERF-1
|
||||
// Commented out by mkirk as part of ORM perf optimizations.
|
||||
// The `MTLSelectorWithCapitalizedKeyPattern` can be quite expensive in aggregate
|
||||
// and we're not using the reflective features that require it.
|
||||
// If we later want to use this feature, we'll need to carefully evaluate the perf
|
||||
// implications on large migrations.
|
||||
/// Merges the value of the given key on the receiver with the value of the same
|
||||
/// key from the given model object, giving precedence to the other model object.
|
||||
- (void)mergeValueForKey:(NSString *)key fromModel:(id<MTLModel>)model;
|
||||
//- (void)mergeValueForKey:(NSString *)key fromModel:(id<MTLModel>)model;
|
||||
// END ORM-PERF-1
|
||||
|
||||
/// Returns the keys for all @property declarations, except for `readonly`
|
||||
/// properties without ivars, or properties on MTLModel itself.
|
||||
+ (NSSet *)propertyKeys;
|
||||
+ (NSSet<NSString *> *)propertyKeys;
|
||||
|
||||
/// Validates the model.
|
||||
///
|
||||
@ -90,7 +97,7 @@ typedef enum : NSUInteger {
|
||||
/// validation
|
||||
///
|
||||
/// Returns YES if the model is valid, or NO if the validation failed.
|
||||
- (BOOL)validate:(NSError **)error;
|
||||
//- (BOOL)validate:(NSError **)error;
|
||||
|
||||
@end
|
||||
|
||||
@ -113,23 +120,31 @@ typedef enum : NSUInteger {
|
||||
/// (like a KVC validation error).
|
||||
///
|
||||
/// Returns an initialized model object, or nil if validation failed.
|
||||
- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error;
|
||||
- (instancetype)initWithDictionary:(NSDictionary<NSString *, id> *)dictionaryValue error:(NSError **)error;
|
||||
|
||||
/// Initializes the receiver with default values.
|
||||
///
|
||||
/// This is the designated initializer for this class.
|
||||
- (instancetype)init;
|
||||
|
||||
// BEGIN ORM-PERF-1
|
||||
// Commented out by mkirk as part of ORM perf optimizations.
|
||||
// The `MTLSelectorWithCapitalizedKeyPattern` can be quite expensive in aggregate
|
||||
// and we're not using the reflective features that require it.
|
||||
// If we later want to use this feature, we'll need to carefully evaluate the perf
|
||||
// implications on large migrations.
|
||||
/// By default, this method looks for a `-merge<Key>FromModel:` method on the
|
||||
/// receiver, and invokes it if found. If not found, and `model` is not nil, the
|
||||
/// value for the given key is taken from `model`.
|
||||
- (void)mergeValueForKey:(NSString *)key fromModel:(id<MTLModel>)model;
|
||||
|
||||
//- (void)mergeValueForKey:(NSString *)key fromModel:(id<MTLModel>)model;
|
||||
//
|
||||
/// Merges the values of the given model object into the receiver, using
|
||||
/// -mergeValueForKey:fromModel: for each key in +propertyKeys.
|
||||
///
|
||||
/// `model` must be an instance of the receiver's class or a subclass thereof.
|
||||
- (void)mergeValuesForKeysFromModel:(id<MTLModel>)model;
|
||||
//- (void)mergeValuesForKeysFromModel:(id<MTLModel>)model;
|
||||
//
|
||||
// END ORM-PERF-1
|
||||
|
||||
/// The storage behavior of a given key.
|
||||
///
|
||||
@ -161,19 +176,29 @@ typedef enum : NSUInteger {
|
||||
|
||||
@end
|
||||
|
||||
/// Implements validation logic for MTLModel.
|
||||
@interface MTLModel (Validation)
|
||||
|
||||
/// Validates the model.
|
||||
///
|
||||
/// The default implementation simply invokes -validateValue:forKey:error: with
|
||||
/// all +propertyKeys and their current value. If -validateValue:forKey:error:
|
||||
/// returns a new value, the property is set to that new value.
|
||||
///
|
||||
/// error - If not NULL, this may be set to any error that occurs during
|
||||
/// validation
|
||||
///
|
||||
/// Returns YES if the model is valid, or NO if the validation failed.
|
||||
- (BOOL)validate:(NSError **)error;
|
||||
|
||||
@end
|
||||
// BEGIN ORM-PERF-2
|
||||
// Commented out by mkirk as part of ORM perf optimizations.
|
||||
//
|
||||
// The validation NSCoding validation reflection used by Mantle is expensive, and
|
||||
// we've never used it.
|
||||
// If we later want to use this feature, we'll need to carefully evaluate the perf
|
||||
// implications on large migrations.
|
||||
//
|
||||
///// Implements validation logic for MTLModel.
|
||||
//@interface MTLModel (Validation)
|
||||
//
|
||||
///// Validates the model.
|
||||
/////
|
||||
///// The default implementation simply invokes -validateValue:forKey:error: with
|
||||
///// all +propertyKeys and their current value. If -validateValue:forKey:error:
|
||||
///// returns a new value, the property is set to that new value.
|
||||
/////
|
||||
///// error - If not NULL, this may be set to any error that occurs during
|
||||
///// validation
|
||||
/////
|
||||
///// Returns YES if the model is valid, or NO if the validation failed.
|
||||
//- (BOOL)validate:(NSError **)error;
|
||||
//
|
||||
//@end
|
||||
//
|
||||
// END ORM-PERF-2
|
||||
|
||||
@ -24,6 +24,24 @@ static void *MTLModelCachedTransitoryPropertyKeysKey = &MTLModelCachedTransitory
|
||||
// property keys.
|
||||
static void *MTLModelCachedPermanentPropertyKeysKey = &MTLModelCachedPermanentPropertyKeysKey;
|
||||
|
||||
// BEGIN ORM-PERF-4
|
||||
// Added by mkirk as part of ORM perf optimizations.
|
||||
//
|
||||
// +dictionaryValueKeys is somewhat expensive, so we follow existing library patterns
|
||||
// to cache the computed reflection on the class via an associated object
|
||||
//
|
||||
// Used to cache the reflection performed in +dictionaryValueKeys
|
||||
static void *MTLModelCachedDictionaryValueKeysKey = &MTLModelCachedDictionaryValueKeysKey;
|
||||
// END ORM-PERF-4
|
||||
|
||||
// BEGIN ORM-PERF-2
|
||||
// Commented out by mkirk as part of ORM perf optimizations.
|
||||
//
|
||||
// The validation NSCoding validation reflection used by Mantle is expensive, and
|
||||
// we've never used it.
|
||||
// If we later want to use this feature, we'll need to carefully evaluate the perf
|
||||
// implications on large migrations.
|
||||
//
|
||||
// Validates a value for an object and sets it if necessary.
|
||||
//
|
||||
// obj - The object for which the value is being validated. This value
|
||||
@ -38,35 +56,36 @@ static void *MTLModelCachedPermanentPropertyKeysKey = &MTLModelCachedPermanentPr
|
||||
//
|
||||
// Returns YES if `value` could be validated and set, or NO if an error
|
||||
// occurred.
|
||||
static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUpdate, NSError **error) {
|
||||
// Mark this as being autoreleased, because validateValue may return
|
||||
// a new object to be stored in this variable (and we don't want ARC to
|
||||
// double-free or leak the old or new values).
|
||||
__autoreleasing id validatedValue = value;
|
||||
|
||||
@try {
|
||||
if (![obj validateValue:&validatedValue forKey:key error:error]) return NO;
|
||||
|
||||
if (forceUpdate || value != validatedValue) {
|
||||
[obj setValue:validatedValue forKey:key];
|
||||
}
|
||||
|
||||
return YES;
|
||||
} @catch (NSException *ex) {
|
||||
NSLog(@"*** Caught exception setting key \"%@\" : %@", key, ex);
|
||||
|
||||
// Fail fast in Debug builds.
|
||||
#if DEBUG
|
||||
@throw ex;
|
||||
#else
|
||||
if (error != NULL) {
|
||||
*error = [NSError mtl_modelErrorWithException:ex];
|
||||
}
|
||||
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
//static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUpdate, NSError **error) {
|
||||
// // Mark this as being autoreleased, because validateValue may return
|
||||
// // a new object to be stored in this variable (and we don't want ARC to
|
||||
// // double-free or leak the old or new values).
|
||||
// __autoreleasing id validatedValue = value;
|
||||
//
|
||||
// @try {
|
||||
// if (![obj validateValue:&validatedValue forKey:key error:error]) return NO;
|
||||
//
|
||||
// if (forceUpdate || value != validatedValue) {
|
||||
// [obj setValue:validatedValue forKey:key];
|
||||
// }
|
||||
//
|
||||
// return YES;
|
||||
// } @catch (NSException *ex) {
|
||||
// NSLog(@"*** Caught exception setting key \"%@\" : %@", key, ex);
|
||||
//
|
||||
// // Fail fast in Debug builds.
|
||||
// #if DEBUG
|
||||
// @throw ex;
|
||||
// #else
|
||||
// if (error != NULL) {
|
||||
// *error = [NSError mtl_modelErrorWithException:ex];
|
||||
// }
|
||||
//
|
||||
// return NO;
|
||||
// #endif
|
||||
// }
|
||||
//}
|
||||
// END ORM-PERF-2
|
||||
|
||||
@interface MTLModel ()
|
||||
|
||||
@ -76,11 +95,11 @@ static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUp
|
||||
|
||||
// Returns a set of all property keys for which
|
||||
// +storageBehaviorForPropertyWithKey returned MTLPropertyStorageTransitory.
|
||||
+ (NSSet *)transitoryPropertyKeys;
|
||||
+ (NSSet<NSString *> *)transitoryPropertyKeys;
|
||||
|
||||
// Returns a set of all property keys for which
|
||||
// +storageBehaviorForPropertyWithKey returned MTLPropertyStoragePermanent.
|
||||
+ (NSSet *)permanentPropertyKeys;
|
||||
+ (NSSet<NSString *> *)permanentPropertyKeys;
|
||||
|
||||
// Enumerates all properties of the receiver's class hierarchy, starting at the
|
||||
// receiver, and continuing up until (but not including) MTLModel.
|
||||
@ -96,8 +115,8 @@ static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUp
|
||||
#pragma mark Lifecycle
|
||||
|
||||
+ (void)generateAndCacheStorageBehaviors {
|
||||
NSMutableSet *transitoryKeys = [NSMutableSet set];
|
||||
NSMutableSet *permanentKeys = [NSMutableSet set];
|
||||
NSMutableSet<NSString *> *transitoryKeys = [NSMutableSet set];
|
||||
NSMutableSet<NSString *> *permanentKeys = [NSMutableSet set];
|
||||
|
||||
for (NSString *propertyKey in self.propertyKeys) {
|
||||
switch ([self storageBehaviorForPropertyWithKey:propertyKey]) {
|
||||
@ -120,7 +139,7 @@ static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUp
|
||||
objc_setAssociatedObject(self, MTLModelCachedPermanentPropertyKeysKey, permanentKeys, OBJC_ASSOCIATION_COPY);
|
||||
}
|
||||
|
||||
+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary error:(NSError **)error {
|
||||
+ (instancetype)modelWithDictionary:(NSDictionary<NSString *, id> *)dictionary error:(NSError **)error {
|
||||
return [[self alloc] initWithDictionary:dictionary error:error];
|
||||
}
|
||||
|
||||
@ -129,7 +148,7 @@ static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUp
|
||||
return [super init];
|
||||
}
|
||||
|
||||
- (instancetype)initWithDictionary:(NSDictionary *)dictionary error:(NSError **)error {
|
||||
- (instancetype)initWithDictionary:(NSDictionary<NSString *, id> *)dictionary error:(NSError **)error {
|
||||
self = [self init];
|
||||
if (self == nil) return nil;
|
||||
|
||||
@ -140,9 +159,18 @@ static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUp
|
||||
__autoreleasing id value = [dictionary objectForKey:key];
|
||||
|
||||
if ([value isEqual:NSNull.null]) value = nil;
|
||||
|
||||
BOOL success = MTLValidateAndSetValue(self, key, value, YES, error);
|
||||
if (!success) return nil;
|
||||
// BEGIN ORM-PERF-2
|
||||
// Commented out by mkirk as part of ORM perf optimizations.
|
||||
//
|
||||
// The validation NSCoding validation reflection used by Mantle is expensive, and
|
||||
// we've never used it.
|
||||
// If we later want to use this feature, we'll need to carefully evaluate the perf
|
||||
// implications on large migrations.
|
||||
//
|
||||
// BOOL success = MTLValidateAndSetValue(self, key, value, YES, error);
|
||||
// if (!success) return nil;
|
||||
[self setValue:value forKey:key];
|
||||
// END ORM-PERF-2
|
||||
}
|
||||
|
||||
return self;
|
||||
@ -172,11 +200,11 @@ static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUp
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSSet *)propertyKeys {
|
||||
NSSet *cachedKeys = objc_getAssociatedObject(self, MTLModelCachedPropertyKeysKey);
|
||||
+ (NSSet<NSString *> *)propertyKeys {
|
||||
NSSet<NSString *> *cachedKeys = objc_getAssociatedObject(self, MTLModelCachedPropertyKeysKey);
|
||||
if (cachedKeys != nil) return cachedKeys;
|
||||
|
||||
NSMutableSet *keys = [NSMutableSet set];
|
||||
NSMutableSet<NSString *> *keys = [NSMutableSet set];
|
||||
|
||||
[self enumeratePropertiesUsingBlock:^(objc_property_t property, BOOL *stop) {
|
||||
NSString *key = @(property_getName(property));
|
||||
@ -193,8 +221,8 @@ static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUp
|
||||
return keys;
|
||||
}
|
||||
|
||||
+ (NSSet *)transitoryPropertyKeys {
|
||||
NSSet *transitoryPropertyKeys = objc_getAssociatedObject(self, MTLModelCachedTransitoryPropertyKeysKey);
|
||||
+ (NSSet<NSString *> *)transitoryPropertyKeys {
|
||||
NSSet<NSString *> *transitoryPropertyKeys = objc_getAssociatedObject(self, MTLModelCachedTransitoryPropertyKeysKey);
|
||||
|
||||
if (transitoryPropertyKeys == nil) {
|
||||
[self generateAndCacheStorageBehaviors];
|
||||
@ -204,8 +232,8 @@ static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUp
|
||||
return transitoryPropertyKeys;
|
||||
}
|
||||
|
||||
+ (NSSet *)permanentPropertyKeys {
|
||||
NSSet *permanentPropertyKeys = objc_getAssociatedObject(self, MTLModelCachedPermanentPropertyKeysKey);
|
||||
+ (NSSet<NSString *> *)permanentPropertyKeys {
|
||||
NSSet<NSString *> *permanentPropertyKeys = objc_getAssociatedObject(self, MTLModelCachedPermanentPropertyKeysKey);
|
||||
|
||||
if (permanentPropertyKeys == nil) {
|
||||
[self generateAndCacheStorageBehaviors];
|
||||
@ -215,12 +243,29 @@ static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUp
|
||||
return permanentPropertyKeys;
|
||||
}
|
||||
|
||||
- (NSDictionary *)dictionaryValue {
|
||||
NSSet *keys = [self.class.transitoryPropertyKeys setByAddingObjectsFromSet:self.class.permanentPropertyKeys];
|
||||
|
||||
return [self dictionaryWithValuesForKeys:keys.allObjects];
|
||||
// BEGIN ORM-PERF-4
|
||||
// Added by mkirk as part of ORM perf optimizations.
|
||||
//
|
||||
// +dictionaryValueKeys is somewhat expensive, so we follow existing library patterns
|
||||
// to cache the computed reflection on the class via an associated object
|
||||
//
|
||||
// Used to cache the reflection performed in +dictionaryValueKeys
|
||||
+ (NSArray<NSString *> *)dictionaryValueKeys {
|
||||
NSArray<NSString *> *dictionaryValueKeys = objc_getAssociatedObject(self, MTLModelCachedDictionaryValueKeysKey);
|
||||
if (!dictionaryValueKeys) {
|
||||
NSSet<NSString *> *keys = [self.class.transitoryPropertyKeys setByAddingObjectsFromSet:self.permanentPropertyKeys];
|
||||
dictionaryValueKeys = keys.allObjects;
|
||||
objc_setAssociatedObject(self, MTLModelCachedDictionaryValueKeysKey, dictionaryValueKeys, OBJC_ASSOCIATION_COPY);
|
||||
}
|
||||
return dictionaryValueKeys;
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, id> *)dictionaryValue {
|
||||
NSArray<NSString *> *keys = self.class.dictionaryValueKeys;
|
||||
return [self dictionaryWithValuesForKeys:keys];
|
||||
}
|
||||
// END ORM-PERF-4
|
||||
|
||||
+ (MTLPropertyStorage)storageBehaviorForPropertyWithKey:(NSString *)propertyKey {
|
||||
objc_property_t property = class_getProperty(self.class, propertyKey.UTF8String);
|
||||
|
||||
@ -250,45 +295,61 @@ static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUp
|
||||
|
||||
#pragma mark Merging
|
||||
|
||||
- (void)mergeValueForKey:(NSString *)key fromModel:(NSObject<MTLModel> *)model {
|
||||
NSParameterAssert(key != nil);
|
||||
// BEGIN ORM-PERF-1
|
||||
// Commented out by mkirk as part of ORM perf optimizations.
|
||||
// The `MTLSelectorWithCapitalizedKeyPattern` can be quite expensive in aggregate
|
||||
// and we're not using the reflective features that require it.
|
||||
// If we later want to use this feature, we'll need to carefully evaluate the perf
|
||||
// implications on large migrations.
|
||||
//
|
||||
//- (void)mergeValueForKey:(NSString *)key fromModel:(NSObject<MTLModel> *)model {
|
||||
// NSParameterAssert(key != nil);
|
||||
//
|
||||
//// SEL selector = MTLSelectorWithCapitalizedKeyPattern("merge", key, "FromModel:");
|
||||
//// if (![self respondsToSelector:selector]) {
|
||||
//// if (model != nil) {
|
||||
//// [self setValue:[model valueForKey:key] forKey:key];
|
||||
//// }
|
||||
////
|
||||
//// return;
|
||||
//// }
|
||||
//
|
||||
// IMP imp = [self methodForSelector:selector];
|
||||
// void (*function)(id, SEL, id<MTLModel>) = (__typeof__(function))imp;
|
||||
// function(self, selector, model);
|
||||
//}
|
||||
//
|
||||
//- (void)mergeValuesForKeysFromModel:(id<MTLModel>)model {
|
||||
// NSSet<NSString *> *propertyKeys = model.class.propertyKeys;
|
||||
//
|
||||
// for (NSString *key in self.class.propertyKeys) {
|
||||
// if (![propertyKeys containsObject:key]) continue;
|
||||
//
|
||||
// [self mergeValueForKey:key fromModel:model];
|
||||
// }
|
||||
//}
|
||||
// END ORM-PERF-1
|
||||
|
||||
SEL selector = MTLSelectorWithCapitalizedKeyPattern("merge", key, "FromModel:");
|
||||
if (![self respondsToSelector:selector]) {
|
||||
if (model != nil) {
|
||||
[self setValue:[model valueForKey:key] forKey:key];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
IMP imp = [self methodForSelector:selector];
|
||||
void (*function)(id, SEL, id<MTLModel>) = (__typeof__(function))imp;
|
||||
function(self, selector, model);
|
||||
}
|
||||
|
||||
- (void)mergeValuesForKeysFromModel:(id<MTLModel>)model {
|
||||
NSSet *propertyKeys = model.class.propertyKeys;
|
||||
|
||||
for (NSString *key in self.class.propertyKeys) {
|
||||
if (![propertyKeys containsObject:key]) continue;
|
||||
|
||||
[self mergeValueForKey:key fromModel:model];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Validation
|
||||
|
||||
- (BOOL)validate:(NSError **)error {
|
||||
for (NSString *key in self.class.propertyKeys) {
|
||||
id value = [self valueForKey:key];
|
||||
|
||||
BOOL success = MTLValidateAndSetValue(self, key, value, NO, error);
|
||||
if (!success) return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
// BEGIN ORM-PERF-2
|
||||
// Commented out by mkirk as part of ORM perf optimizations.
|
||||
//
|
||||
// The validation NSCoding validation reflection used by Mantle is expensive, and
|
||||
// we've never used it.
|
||||
// If we later want to use this feature, we'll need to carefully evaluate the perf
|
||||
// implications on large migrations.
|
||||
//#pragma mark Validation
|
||||
//
|
||||
//- (BOOL)validate:(NSError **)error {
|
||||
// for (NSString *key in self.class.propertyKeys) {
|
||||
// id value = [self valueForKey:key];
|
||||
//
|
||||
// BOOL success = MTLValidateAndSetValue(self, key, value, NO, error);
|
||||
// if (!success) return NO;
|
||||
// }
|
||||
//
|
||||
// return YES;
|
||||
//}
|
||||
// END ORM-PERF-2
|
||||
|
||||
#pragma mark NSCopying
|
||||
|
||||
@ -301,9 +362,13 @@ static BOOL MTLValidateAndSetValue(id obj, NSString *key, id value, BOOL forceUp
|
||||
#pragma mark NSObject
|
||||
|
||||
- (NSString *)description {
|
||||
NSDictionary *permanentProperties = [self dictionaryWithValuesForKeys:self.class.permanentPropertyKeys.allObjects];
|
||||
#if DEBUG
|
||||
NSDictionary<NSString *, id> *permanentProperties = [self dictionaryWithValuesForKeys:self.class.permanentPropertyKeys.allObjects];
|
||||
|
||||
return [NSString stringWithFormat:@"<%@: %p> %@", self.class, self, permanentProperties];
|
||||
#else
|
||||
return [NSString stringWithFormat:@"<%@: %p>", self.class, self];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
|
||||
@ -18,6 +18,14 @@
|
||||
/// selector.
|
||||
SEL MTLSelectorWithKeyPattern(NSString *key, const char *suffix) __attribute__((pure, nonnull(1, 2)));
|
||||
|
||||
|
||||
// BEGIN ORM-PERF-1
|
||||
// Commented out by mkirk as part of ORM perf optimizations.
|
||||
// The `MTLSelectorWithCapitalizedKeyPattern` can be quite expensive in aggregate
|
||||
// and we're not using the reflective features that require it.
|
||||
// If we later want to use this feature, we'll need to carefully evaluate the perf
|
||||
// implications on large migrations.
|
||||
//
|
||||
/// Creates a selector from a key and a constant prefix and suffix.
|
||||
///
|
||||
/// prefix - A string to prepend to the key as part of the selector.
|
||||
@ -28,4 +36,6 @@ SEL MTLSelectorWithKeyPattern(NSString *key, const char *suffix) __attribute__((
|
||||
///
|
||||
/// Returns a selector, or NULL if the input strings cannot form a valid
|
||||
/// selector.
|
||||
SEL MTLSelectorWithCapitalizedKeyPattern(const char *prefix, NSString *key, const char *suffix) __attribute__((pure, nonnull(1, 2, 3)));
|
||||
//SEL MTLSelectorWithCapitalizedKeyPattern(const char *prefix, NSString *key, const char *suffix) __attribute__((pure, nonnull(1, 2, 3)));
|
||||
//
|
||||
// END ORM-PERF-1
|
||||
|
||||
@ -24,27 +24,34 @@ SEL MTLSelectorWithKeyPattern(NSString *key, const char *suffix) {
|
||||
return sel_registerName(selector);
|
||||
}
|
||||
|
||||
SEL MTLSelectorWithCapitalizedKeyPattern(const char *prefix, NSString *key, const char *suffix) {
|
||||
NSUInteger prefixLength = strlen(prefix);
|
||||
NSUInteger suffixLength = strlen(suffix);
|
||||
|
||||
NSString *initial = [key substringToIndex:1].uppercaseString;
|
||||
NSUInteger initialLength = [initial maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSString *rest = [key substringFromIndex:1];
|
||||
NSUInteger restLength = [rest maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
char selector[prefixLength + initialLength + restLength + suffixLength + 1];
|
||||
memcpy(selector, prefix, prefixLength);
|
||||
|
||||
BOOL success = [initial getBytes:selector + prefixLength maxLength:initialLength usedLength:&initialLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, initial.length) remainingRange:NULL];
|
||||
if (!success) return NULL;
|
||||
|
||||
success = [rest getBytes:selector + prefixLength + initialLength maxLength:restLength usedLength:&restLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, rest.length) remainingRange:NULL];
|
||||
if (!success) return NULL;
|
||||
|
||||
memcpy(selector + prefixLength + initialLength + restLength, suffix, suffixLength);
|
||||
selector[prefixLength + initialLength + restLength + suffixLength] = '\0';
|
||||
|
||||
return sel_registerName(selector);
|
||||
}
|
||||
// BEGIN ORM-PERF-1
|
||||
// Commented out by mkirk as part of ORM perf optimizations.
|
||||
// The `MTLSelectorWithCapitalizedKeyPattern` can be quite expensive in aggregate
|
||||
// and we're not using the reflective features that require it.
|
||||
// If we later want to use this feature, we'll need to carefully evaluate the perf
|
||||
// implications on large migrations.
|
||||
//SEL MTLSelectorWithCapitalizedKeyPattern(const char *prefix, NSString *key, const char *suffix) {
|
||||
// NSUInteger prefixLength = strlen(prefix);
|
||||
// NSUInteger suffixLength = strlen(suffix);
|
||||
//
|
||||
// NSString *initial = [key substringToIndex:1].uppercaseString;
|
||||
// NSUInteger initialLength = [initial maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
||||
//
|
||||
// NSString *rest = [key substringFromIndex:1];
|
||||
// NSUInteger restLength = [rest maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
||||
//
|
||||
// char selector[prefixLength + initialLength + restLength + suffixLength + 1];
|
||||
// memcpy(selector, prefix, prefixLength);
|
||||
//
|
||||
// BOOL success = [initial getBytes:selector + prefixLength maxLength:initialLength usedLength:&initialLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, initial.length) remainingRange:NULL];
|
||||
// if (!success) return NULL;
|
||||
//
|
||||
// success = [rest getBytes:selector + prefixLength + initialLength maxLength:restLength usedLength:&restLength encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0, rest.length) remainingRange:NULL];
|
||||
// if (!success) return NULL;
|
||||
//
|
||||
// memcpy(selector + prefixLength + initialLength + restLength, suffix, suffixLength);
|
||||
// selector[prefixLength + initialLength + restLength + suffixLength] = '\0';
|
||||
//
|
||||
// return sel_registerName(selector);
|
||||
//}
|
||||
// END ORM-PERF-1
|
||||
|
||||
Loading…
Reference in New Issue
Block a user