7

This is a mystery:

I'm invoking setPrimitiveValue:forKey: on an NSManagedObject. The key is a legit, persistent, modeled attribute of the object. However, setPrimitiveValue:forKey: fails, often setting the value for a different, arbitrary attribute. The docs say this behavior is expected when invoking setPrimitiveValue:forKey: for an unmodeled key. So it seems Core Data thinks the key is unmodeled.

The strange part:

When the key is hardcoded as a string literal, the primitive value is indeed set successfully. It only fails when the key is a variable. The variable I'm using happens to be passed from the keyPath argument of observeValueForKeyPath:ofObject:change:context:

The keyPath variable is the same as the string literal. isEqual: returns true and the hash values are equal. The keyPath variable is of type __NSCFString. Does anyone know why setPrimitiveValue:forKey: would behave any differently? (This behavior is on OS X 10.9.1)


An update with better information:

The misbehaving key traced back to a string loaded from a file on disk. The example below is an isolated case. If the attribute string "mainAttr" is written to disk and read back in, then setPrimitiveValue:forKey: sets the value for the wrong attribute, not "mainAttr".

Core data object:

@interface Boo : NSManagedObject
@property (nonatomic, retain) NSNumber * mainAttr;
@property (nonatomic, retain) NSNumber * attr1;
@property (nonatomic, retain) NSNumber * attr2;
@property (nonatomic, retain) NSNumber * attr3;
@end

-

#import "Boo.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
            NSManagedObjectContext *context = managedObjectContext();
    NSString *key = @"mainAttr";

    // write to disk, read back in
    NSString *path = [@"~/Desktop/test.txt" stringByExpandingTildeInPath];
    [key writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
    key = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];

    Boo *boo = [NSEntityDescription insertNewObjectForEntityForName:@"Boo" inManagedObjectContext:context];
    [boo setPrimitiveValue:@(5) forKey:key];

    NSLog(@"Boo: %@", boo);
    }
    return 0;
}
bllakjakk
  • 5,045
  • 1
  • 18
  • 28
Steveo
  • 2,238
  • 1
  • 21
  • 34
  • We've seen a similar behavior on iOS as well, failing with an "generic" validation error when saving. Your question brings an interesting view of why this happens. – Léo Natan Dec 18 '13 at 21:10
  • 2
    I don't know but I'd be interested in seeing code from `observeValueForKeyPath:ofObject:change:context:` where the problem occurs. – Tom Harrington Dec 18 '13 at 21:31
  • Can you put this into a test project? – Marcus S. Zarra Dec 20 '13 at 15:51
  • I'm trying to isolate it in a test project but of course the behavior isn't manifesting there...yet – Steveo Dec 20 '13 at 22:09
  • Updated original post with example – Steveo Dec 21 '13 at 01:13
  • Please file a bug and include your sample script (might also want to instruct them to use CodeRunner). It is likely that CoreData is becoming confused by the memory address or pointer of 'key'. – ImHuntingWabbits Dec 31 '13 at 16:38
  • in case of -primitiveValueForKey: and -setPrimitiveValue:forKey:; Core Data doesn't just need to know when you're going to modify an attribute; it needs to know when you're going to access it as well, so it can implement faulting. - (NSString *)name { [self willAccessValueForKey:@"name"]; NSString *value = [self primitiveValueForKey:@"name"]; [self didAccessValueForKey:@"name"]; } - (void)setName:(NSString *)value { [self willChangeValueForKey:@"name"]; [self setPrimitiveValue:value forKey:@"name"]; [self didChangeValueForKey:@"name"]; } – Lightygalaxy Oct 19 '14 at 17:19

1 Answers1

1

You need the below 3 statements to set the value. Try it.

[self willChangeValueForKey:key];
[boo setPrimitiveValue:@(5) forKey:key];
[self didChangeValueForKey:key];
bllakjakk
  • 5,045
  • 1
  • 18
  • 28