Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add property copy and customize encoding/decoding methods #516

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
19 changes: 19 additions & 0 deletions YYKit/Model/NSObject+YYModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (nullable id)modelCopy;

/**
SHADOW copy properties from sourceObj to self
Roen(https://github.com/Roen-Ro) added/2018.12.24

@param sourceObj the copy source, type safe
*/
-(void)copyPropertiesFromSourceObject:(id)sourceObj;

/**
Encode the receiver's properties to a coder.

Expand Down Expand Up @@ -436,6 +444,17 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;

/**
Do customer encode/decode, for subclass to override
Roen(https://github.com/Roen-Ro) added/2018.12.24

@param propertyKey the property key
@param aCoder /aDecoder the decoder/encoder
@return return YES for the properties if you wish to do customer encoding/decoding, else return NO, default return NO
*/
-(BOOL)shouldCustomEncodeValueForKey:(NSString *)propertyKey withCoder:(NSCoder *)aCoder;
-(BOOL)shouldCustomDecodeValueForKey:(NSString *)propertyKey withCoder:(NSCoder *)aDecoder;

@end

NS_ASSUME_NONNULL_END
135 changes: 90 additions & 45 deletions YYKit/Model/NSObject+YYModel.m
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {
/// Parse string to date.
static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) {
typedef NSDate* (^YYNSDateParseBlock)(NSString *string);
#define kParserNum 34
#define kParserNum 34
static YYNSDateParseBlock blocks[kParserNum + 1] = {0};
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Expand Down Expand Up @@ -165,12 +165,12 @@ static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter2.dateFormat = @"yyyy-MM-dd HH:mm:ss";

NSDateFormatter *formatter3 = [[NSDateFormatter alloc] init];
formatter3.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter3.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter3.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS";

NSDateFormatter *formatter4 = [[NSDateFormatter alloc] init];
formatter4.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter4.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
Expand All @@ -183,7 +183,7 @@ static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {
return [formatter2 dateFromString:string];
}
};

blocks[23] = ^(NSString *string) {
if ([string characterAtIndex:10] == 'T') {
return [formatter3 dateFromString:string];
Expand All @@ -205,11 +205,11 @@ static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";

NSDateFormatter *formatter2 = [NSDateFormatter new];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZ";

blocks[20] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[24] = ^(NSString *string) { return [formatter dateFromString:string]?: [formatter2 dateFromString:string]; };
blocks[25] = ^(NSString *string) { return [formatter dateFromString:string]; };
Expand All @@ -225,11 +225,11 @@ static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy";

NSDateFormatter *formatter2 = [NSDateFormatter new];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.dateFormat = @"EEE MMM dd HH:mm:ss.SSS Z yyyy";

blocks[30] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[34] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
}
Expand All @@ -239,7 +239,7 @@ static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {
YYNSDateParseBlock parser = blocks[string.length];
if (!parser) return nil;
return parser(string);
#undef kParserNum
#undef kParserNum
}


Expand Down Expand Up @@ -1152,7 +1152,7 @@ static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, voi
}

/**
Returns a valid JSON object (NSArray/NSDictionary/NSString/NSNumber/NSNull),
Returns a valid JSON object (NSArray/NSDictionary/NSString/NSNumber/NSNull),
or nil if an error occurs.

@param model Model, can be nil.
Expand Down Expand Up @@ -1307,7 +1307,7 @@ static id ModelToJSONObjectRecursive(NSObject *model) {
case YYEncodingTypeNSString: case YYEncodingTypeNSMutableString: {
return [NSString stringWithFormat:@"\"%@\"",model];
}

case YYEncodingTypeNSValue:
case YYEncodingTypeNSData: case YYEncodingTypeNSMutableData: {
NSString *tmp = model.description;
Expand Down Expand Up @@ -1366,7 +1366,7 @@ static id ModelToJSONObjectRecursive(NSObject *model) {
}
return desc;
}

default: {
NSMutableString *desc = [NSMutableString new];
[desc appendFormat:@"<%@: %p>", model.class, model];
Expand Down Expand Up @@ -1544,85 +1544,124 @@ - (NSString *)modelToJSONString {
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}

- (id)modelCopy{
if (self == (id)kCFNull) return self;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) return [self copy];
//Roen(https://github.com/Roen-Ro) added/2018.12.24
-(void)copyPropertiesFromSourceObject:(NSObject *)sourceObj {

NSObject *one = [self.class new];
for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_getter || !propertyMeta->_setter) continue;
if (self == (id)kCFNull) return;

_YYModelMeta *selfModelMeta = [_YYModelMeta metaWithClass:self.class];

_YYModelMeta *srcModelMeta = [_YYModelMeta metaWithClass:sourceObj.class];
if(!srcModelMeta)
return;

NSMutableDictionary *mDic = [NSMutableDictionary dictionaryWithCapacity:srcModelMeta->_keyMappedCount];
for (_YYModelPropertyMeta *propertyMeta in srcModelMeta->_allPropertyMetas) {
[mDic setObject:propertyMeta forKey:propertyMeta->_name];
}

NSObject *one = self;
for (_YYModelPropertyMeta *selfPropertyMeta in selfModelMeta->_allPropertyMetas) {
if (!selfPropertyMeta->_setter)
continue;

if (propertyMeta->_isCNumber) {
switch (propertyMeta->_type & YYEncodingTypeMask) {
_YYModelPropertyMeta *srcPropertyMeta = [mDic objectForKey:selfPropertyMeta->_name];

if(!srcPropertyMeta
|| !srcPropertyMeta->_getter
|| (srcPropertyMeta->_type != selfPropertyMeta->_type))
continue;

if (selfPropertyMeta->_isCNumber) {
switch (selfPropertyMeta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool: {
bool num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
bool num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)sourceObj, srcPropertyMeta->_getter);
((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)one, selfPropertyMeta->_setter, num);
} break;
case YYEncodingTypeInt8:
case YYEncodingTypeUInt8: {
uint8_t num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
uint8_t num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)sourceObj, srcPropertyMeta->_getter);
((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)one, selfPropertyMeta->_setter, num);
} break;
case YYEncodingTypeInt16:
case YYEncodingTypeUInt16: {
uint16_t num = ((uint16_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
uint16_t num = ((uint16_t (*)(id, SEL))(void *) objc_msgSend)((id)sourceObj, srcPropertyMeta->_getter);
((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)one, selfPropertyMeta->_setter, num);
} break;
case YYEncodingTypeInt32:
case YYEncodingTypeUInt32: {
uint32_t num = ((uint32_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
uint32_t num = ((uint32_t (*)(id, SEL))(void *) objc_msgSend)((id)sourceObj, srcPropertyMeta->_getter);
((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)one, selfPropertyMeta->_setter, num);
} break;
case YYEncodingTypeInt64:
case YYEncodingTypeUInt64: {
uint64_t num = ((uint64_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
uint64_t num = ((uint64_t (*)(id, SEL))(void *) objc_msgSend)((id)sourceObj, srcPropertyMeta->_getter);
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)one, selfPropertyMeta->_setter, num);
} break;
case YYEncodingTypeFloat: {
float num = ((float (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, float))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
float num = ((float (*)(id, SEL))(void *) objc_msgSend)((id)sourceObj, srcPropertyMeta->_getter);
((void (*)(id, SEL, float))(void *) objc_msgSend)((id)one, selfPropertyMeta->_setter, num);
} break;
case YYEncodingTypeDouble: {
double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, double))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)sourceObj, srcPropertyMeta->_getter);
((void (*)(id, SEL, double))(void *) objc_msgSend)((id)one, selfPropertyMeta->_setter, num);
} break;
case YYEncodingTypeLongDouble: {
long double num = ((long double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
long double num = ((long double (*)(id, SEL))(void *) objc_msgSend)((id)sourceObj, srcPropertyMeta->_getter);
((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)one, selfPropertyMeta->_setter, num);
} // break; commented for code coverage in next line
default: break;
}
} else {
switch (propertyMeta->_type & YYEncodingTypeMask) {
switch (selfPropertyMeta->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject:
case YYEncodingTypeClass:
case YYEncodingTypeBlock: {
id value = ((id (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)one, propertyMeta->_setter, value);
id value = ((id (*)(id, SEL))(void *) objc_msgSend)((id)sourceObj, srcPropertyMeta->_getter);
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)one, selfPropertyMeta->_setter, value);
} break;
case YYEncodingTypeSEL:
case YYEncodingTypePointer:
case YYEncodingTypeCString: {
size_t value = ((size_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, size_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, value);
size_t value = ((size_t (*)(id, SEL))(void *) objc_msgSend)((id)sourceObj, srcPropertyMeta->_getter);
((void (*)(id, SEL, size_t))(void *) objc_msgSend)((id)one, selfPropertyMeta->_setter, value);
} break;
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
@try {
NSValue *value = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
NSValue *value = [sourceObj valueForKey:NSStringFromSelector(srcPropertyMeta->_getter)];
if (value) {
[one setValue:value forKey:propertyMeta->_name];
[one setValue:value forKey:selfPropertyMeta->_name];
}
} @catch (NSException *exception) {}
} // break; commented for code coverage in next line
default: break;
}
}
}
}
- (id)modelCopy{
if (self == (id)kCFNull) return self;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) return [self copy];

NSObject *one = [self.class new];
[one copyPropertiesFromSourceObject:self];

return one;
}

//Roen(https://github.com/Roen-Ro) added/2018.12.24
-(BOOL)shouldCustomEncodeValueForKey:(NSString *)propertyKey withCoder:(NSCoder *)aCoder
{
return NO;
}

-(BOOL)shouldCustomDecodeValueForKey:(NSString *)propertyKey withCoder:(NSCoder *)aDecoder
{
return NO;
}

- (void)modelEncodeWithCoder:(NSCoder *)aCoder {
if (!aCoder) return;
if (self == (id)kCFNull) {
Expand All @@ -1639,6 +1678,9 @@ - (void)modelEncodeWithCoder:(NSCoder *)aCoder {
for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_getter) return;

if([self shouldCustomEncodeValueForKey:propertyMeta->_name withCoder:aCoder])
continue;

if (propertyMeta->_isCNumber) {
NSNumber *value = ModelCreateNumberFromProperty(self, propertyMeta);
if (value != nil) [aCoder encodeObject:value forKey:propertyMeta->_name];
Expand Down Expand Up @@ -1682,13 +1724,16 @@ - (void)modelEncodeWithCoder:(NSCoder *)aCoder {

- (id)modelInitWithCoder:(NSCoder *)aDecoder {
if (!aDecoder) return self;
if (self == (id)kCFNull) return self;
if (self == (id)kCFNull) return self;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) return self;

for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_setter) continue;

if([self shouldCustomDecodeValueForKey:propertyMeta->_name withCoder:aDecoder])
continue;

if (propertyMeta->_isCNumber) {
NSNumber *value = [aDecoder decodeObjectForKey:propertyMeta->_name];
if ([value isKindOfClass:[NSNumber class]]) {
Expand Down