Core Data: setPrimitiveValue:forKey: behaves really weirdly - ios

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;
}

You need the below 3 statements to set the value. Try it.
[self willChangeValueForKey:key];
[boo setPrimitiveValue:#(5) forKey:key];
[self didChangeValueForKey:key];

Related

How to properly use generics on obj-c?

With the new xcode7 Apple introduced generics and nullability to Objective-C ( Developer guide )
But it seems to be very different from what we have on swift.
Nullability:
- (nonnull NSString *)something {
return nil;
}
This should raise a warning! And you can even assign the return value of this method to a nonnull variable like:
//#property (copy, nonnull) NSString *name
obj.name = [obj something];
Generics:
Looking this example:
#property (nonatomic, strong, nonnull) NSMutableArray <UIView *> *someViews;
a warning is raised when something different from a UIView is inserted on the array
[self.someViews addObject:#"foobar"]; //<- this raises an error
but not in this case:
self.someViews = [#[#"foobar"] mutableCopy];
nor in this case:
NSString *str = [self.someViews firstObject];
So the question is, I'm using generics and nullability in a wrong way or they are far away from the Swift implementation?
self.someViews = [#[#"foobar"] mutableCopy];
mutableCopy is inherited from NSObject, where it is declared to return id. It is not declared by NSArray specifically and NSArray des not decide the return type.
NSString *str = [self.someViews firstObject];
This does give a warning for me.

convert NSString to int16_t and insert them inside core data

hello what i am trying to do is to taking a NSString value from UITextField and convert this value into int16_t to save it inside my core data after many hour of googling some of the stack people said that core data could not accept primitive data type such as int Boolean etc... and should receive object type such as NSNumber NSInteger etc... here is my code:
CoreData *coreDat = [CoreData defaultStack];
numberEntity *entry = [NSEntityDescription insertNewObjectForEntityForName:#"NumberEntity" inManagedObjectContext:coreDat.managedObjectContext]
;
[entry setNumber:(NSNumber *)self.numberTextField.text];
and here is the entity class:
numberEntity.m
#import "numberEntity.h"
#implementation numberEntity
#dynamic number;
#end
numberEntity.h
#import <Foundation/Foundation.h>
#interface numberEntity : NSObject
#property (nonatomic,strong) NSNumber *number;
#end
and for my core data is integer 16
i could not post the image due to my low reputation i am so sorry also i am still new to stackoverflow.com
but after running this code i got this exception :
Unacceptable type of value for attribute: property = "number"; desired type = NSNumber; given type = __NSCFString; value = 1.
hope you will help me forget to mention that i am still newbie in IOS development and i came from android development
Thanks in advance
Just replace this line [entry setNumber:(NSNumber *)self.numberTextField.text]; to [entry setNumber:#([self.numberTextField.text intValue])]; .Hope its working.

Serialising between versions

How do I deserialise the object which got modified in new version.
For Example
I have a person class in my project for version 1.0.
Array of Person objects gets serialized when app is terminated
#interface Person : NSObject <NSCoding>
#property NSString *name;
#property NSUInteger age;
#property NSString *location;
#end
This is how I am serialising my data
- (void)serializePersonList {
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:personList];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:encodedObject forKey:kPersonKey];
[defaults synchronize];
}
Now I am planning to release version 2. And Person Class has been evolved to
#interface Person : NSObject <NSCoding>
#property NSString *name;
#property NSUInteger age;
#property NSString *phone;
#property KSAddress *location;
#end
Note
Type of location has beed updated to KSAddress
phone attribute has been added.
How do I make my app deserialize the old Person data ?
Hopefully you're using a keyed archiver. You write the code to connect the archived data for each key to the instance variable it belongs to. As you have changed type your code now needs to read the data into an id local variable, check the data type (isKindOfClass) and then either store the value or convert it to your new format and then store it.

NSNumber assignment issues. Wrong number being assigned

I'm receiving data in JSON from an API and after converting the data into custom class I'm attempting to write certain properties into a core-data db.
My code is:
NSLog(#"topPost: %#; post(in): %#", topPostsEntity.totalAgainst,post.totalAgainst);
topPostsEntity.totalAgainst = post.totalAgainst;
NSLog(#"topPost: %#; post(in): %#", topPostsEntity.totalAgainst,post.totalAgainst);
topPostEntity is a Core-Data entity.
topPostEntity.totalAgainst is defined as follows:
#property (nonatomic, retain) NSNumber * totalAgainst;
Nothing special in the getter:
- (NSNumber *)totalAgainst
{
if (!_totalAgainst) _totalAgainst = [[NSNumber alloc]init];
return _totalAgainst;
}
post.totalAgainst is also defined as an NSNumber as follows:
#property (nonatomic,strong) NSNumber *totalAgainst;
To my shock this is what the log reads:
2013-11-02 00:42:42.476 kello[6260:70b] topPost: 0; post(in): 40000
2013-11-02 00:42:42.476 kello[6260:70b] topPost: -25536; post(in): 40000
How does assigning 40000 to topPostEntity.totalAgainst make it store -25536?
Solved the problem.
I had set the core-data entity to Integer 16. I changed this to Integer 64 and that resolved the problem. I'm not sure how anyone's going to find this q to help their problem, but if so - cheers.

Why do I have to retain a property despite using (nonatomic, retain)?

Short Version:
I define a property with (nonatomic, retain) and assumed that the property would be retained. But unless I call retain when assigning a dictionary to the property, The app crashes with an EXEC BAD ACCESS error.
Long Version:
I have a singleton which has a dictionary. The header is defined like this
#interface BRManager : NSObject {
}
#property (nonatomic, retain) NSMutableDictionary *gameState;
+ (id)sharedManager;
- (void) saveGameState;
#end
In the implementation file, I have a method that's called in the init. This method loads a plist form the bundle and makes a copy of it in the users documents folder on the device.
- (void) loadGameState
{
NSFileManager *fileManger=[NSFileManager defaultManager];
NSError *error;
NSArray *pathsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *doumentDirectoryPath=[pathsArray objectAtIndex:0];
NSString *destinationPath= [doumentDirectoryPath stringByAppendingPathComponent:#"gameState.plist"];
NSLog(#"plist path %#",destinationPath);
if (![fileManger fileExistsAtPath:destinationPath]){
NSString *sourcePath=[[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:#"gameStateTemplate.plist"];
[fileManger copyItemAtPath:sourcePath toPath:destinationPath error:&error];
gameState = [NSMutableDictionary dictionaryWithContentsOfFile:sourcePath];
}else{
gameState = [NSMutableDictionary dictionaryWithContentsOfFile:destinationPath];
}
}
Now here's how I thought this should work. In the header I define the gameState property with (nonatomic, retain). I assumed (probably incorrectly) that 'retain' meant that the gameState dictionary would be retained. However, I have another method in my singleton (saveGameState) that get's called when the AppDelegate -> 'applicationWillResignActive'.
- (void) saveGameState
{
NSArray *pathsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *doumentDirectoryPath=[pathsArray objectAtIndex:0];
NSString *plistPath = [doumentDirectoryPath stringByAppendingPathComponent:#"gameState.plist"];
[gameState writeToFile:plistPath atomically:YES];
}
This throws an EXEC BAD ACCESS error on gameState. If I modify loadGameState to retain the gameState dictionary, everything works as it should. eg:
gameState = [[NSMutableDictionary dictionaryWithContentsOfFile:sourcePath] retain];
I'm guessing this is the correct behaviour, but why? Does (nonatomic, retain) not mean what I think it means, or is something else at play here?
I've not really grok'd memory management yet, so I stumble on this stuff all the time.
You must use the accessor:
self.gameState = [NSMutableDictionary dictionaryWithContentsOfFile:sourcePath];
or (is equivalent to):
[self setGameState:[NSMutableDictionary dictionaryWithContentsOfFile:sourcePath]];
instead of
gameState = [NSMutableDictionary dictionaryWithContentsOfFile:sourcePath];
which only sets the ivar without any notion of property.
Where do you declare gameState as an ivar? I'm presuming you do so in the implementation.
The real problem is that in your implementation, you access gameState directly and don't actually invoke the property you've declared. To do so you must send self the appropriate message:
[self gameState]; // invokes the synthesized getter
[self setGameState:[NSMutableDictionary dictionaryWithContentsOfFile:sourcePath]]; // invokes the synthesized setter -- solves your problem
or
whatever = self.gameState; // invokes the getter
self.gameState = [NSMutableDictionary dictionaryWithContentsOfFile:sourcePath]; // invokes the synthesized setter -- solves your problem
Make sure you get around to groking that memory management literature... this is a very basic question which, according to the strict rules of StackOverflow, I shouldn't be answering. Good luck!

Resources