Core Data: setPrimitiveValue:forKey: behaves really weirdly
Asked Answered
L

1

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;
}
Lifework answered 18/12, 2013 at 20:58 Comment(7)
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.Krypton
I don't know but I'd be interested in seeing code from observeValueForKeyPath:ofObject:change:context: where the problem occurs.Lapse
Can you put this into a test project?Resistant
I'm trying to isolate it in a test project but of course the behavior isn't manifesting there...yetLifework
Updated original post with exampleLifework
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'.Rashad
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"]; }Essieessinger
I
1

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

[self willChangeValueForKey:key];
[boo setPrimitiveValue:@(5) forKey:key];
[self didChangeValueForKey:key];
Irs answered 19/10, 2014 at 16:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.