jsonmodel / jsonmodel

Magical Data Modeling Framework for JSON - allows rapid creation of smart data models. You can use it in your iOS, macOS, watchOS and tvOS apps.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

-valueForKey: the method may cause a bug

CoderDHT opened this issue · comments

// .h
@interface WMActivity : JSONModel

@property (nonatomic, copy) NSString *theme;
@property (nonatomic, copy) NSString *content;
@property (nonatomic, strong) NSNumber *getStartDate;
@property (nonatomic, strong) NSNumber *startDate;

@end

// .m
@implementation WMActivity

+ (BOOL)propertyIsOptional:(NSString *)propertyName {
    return YES;
}

@end

// using JSONModel
NSDictionary *dict = @{@"getStartDate" : @(1563199999), @"startDate" : @(1563199999)};
WMActivity *model = [[WMActivity alloc] initWithDictionary:dict error:nil];
NSLog(@"startDate == %@, getStartDate == %@", model.startDate, model.getStartDate);
NSLog(@"%@", model);

Snip20190715

Analyze

In fact, it's the -valueForKey: method that causes this result.
Apple notes this method like this:

The default implementation of this method does the following:

  1. Searches the class of the receiver for an accessor method whose name matches the pattern -get<Key>, -<key>, or -is<Key>, in that order. If such a method is found it is invoked.

Before JSONModel assigns startDate , it use the [self valueForKey:@"startDate"] method to determine whether the value to be assigned is equal to it, but it actually takes the value of getStartDate , so they're equal, and then it doesn't assign startDate .
The -description method of the JSONModel also use -valueForKey: , so the output startDate is valued.

Solution

// .m
// override and try calling -<key> first
- (id)valueForKey:(NSString *)key {
    SEL getter = NSSelectorFromString(key);
    if ([self respondsToSelector:getter]) {
        IMP imp = [self methodForSelector:getter];
        id (*func)(id, SEL) = (void *)imp;
        return func(self, getter);
    }
    return [super valueForKey:key];
}

PS: The problem occurs only if the values of startDate and getStartDate are equal and getStartDate is assigned first, which is a very special case, you decide whether to deal with it or not. :)