how does CoreData manage class relationship creation? - core-data

I have an entity class, correctly defined in a managed context.
This class has a one-to-many relationship with another class.
Xcode 4 graphic facilities correctly created the derived classes, and the relationship is represented by a NSSet.
I am wondering how the creation of the relation classes is managed.
I mean, for creating the main entity I am using the
NSManagedObject *newEntity = [NSEntityDescription
insertNewObjectForEntityForName:#"EntityName"
inManagedObjectContext:context];
But what about the relationship in NSSet ? Do I need to create it in the same way as a parent entity and store it regularly in NSSet ?
NSManagedObject *child = [NSEntityDescription insertNewObjectForEntityForName:#"ChildName" inManagedObjectContext:context];
NSSet *childSet = [..set creation with child..];
newEntity.child = childSet;
// save newEntity in context
If yes, because NSSet is an object why doesn't it need to be created starting from the context ? Such question could be applied to all the 'normal' properties in the entity, an NSString is an object too, why a simple newEntity.prop=#"" is enough ?

For to-many relationships NSManagedObject has a mutableSetValueForKey: method that returns the set you would use. With newEntity and child defined as above you'd do something like this:
NSMutableSet *childObjects = [newEntity mutableSetValueForKey:#"childRelationship"];
[childObjects addObject:child];
But you said you had Xcode generate custom subclasses for your Core Data entities, so you have a convenience method defined in the Parent class which would be named something like addChildObject:. Using that you could replace the above with a simpler version, but you'll also need to declare newEntity as an instance of this subclass instead of as a generic NSManagedObject:
Parent *parent = [NSEntityDescription insertNewObjectForEntityForName:#"EntityName" inManagedObjectContext:context];
NSManagedObject *child = [NSEntityDescription insertNewObjectForEntityForName:#"ChildName" inManagedObjectContext:context];
[parent addChildObject:child];

Related

Properly use a singleton in objective c

I´m lost on this one, already read some things all over the internet but my problem is that i need to understand how to properly use the singleton. My problem is, at some point in my app i do what is below:
myVariable = [NSEntityDescription insertNewObjectForEntityForName:#"Entity"
inManagedObjectContext:context];
I need to preserve myVariable and use it in other views, and i read somewhere that this is the best way if i want to use a variable through all my views. I have followed this example but i really don´t know how to use it, can someone explain it to me?:
#interface DataLoader : NSObject {
NSString *someProperty;
//(i think i need myVariable here, and not type NSString)
}
#property (nonatomic, retain) NSString *someProperty;
+ (id)sharedManager;
#end
#implementation DataLoader
+(id)sharedInstance {
static dispatch_once_t p=0;
__strong static id _sharedObject = nil;
dispatch_once(&p, ^{
_sharedObject = [[self alloc]init];
});
return _sharedObject;
}
#end
How can i set myVariable and then in another views use it?
Regards
The usual way is to have the controllers pass the variable on to the next one whenever they are pushed on the navigation stack, e.g. in prepareForSegue:. Just give your view controllers a strong #property to keep track of it.
SomeViewController *nextVC = segue.destinationController;
nextVC.myVariable = self.myVariable;
That is how it is done in many instances of Apple's sample code with managed object context, it certainly is good pattern.

CoreData: error: Failed to call designated initializer on NSManagedObject class 'Collect'

In order to wrap some logic to model, I plan to put NSManagedObjectContext in one NSManagedObject model, so I can handle many common logic in one model:
#interface Collect : NSManagedObject{
NSManagedObjectContext *managedObjectContext;
}
#property (nonatomic, retain) NSNumber *created_at;
#property (nonatomic, retain) NSString *name;
- (void) initContext;
#end
#implementation Collect
#dynamic created_at;
#dynamic name;
- (void) initContext{
if (managedObjectContext == nil)
{
managedObjectContext = [MyAppDelegate managedObjectContext];
}
}
#end
And I use it by:
Collect *collect = [[Collect new] autorelease];
[collect initContext];
But when run it shows:CoreData: error: Failed to call designated initializer on NSManagedObject class 'Collect'.
I want to know How to add NSManagedObjectContext instance to NSManagedObject model correctly ?
You cannot add a managed object context to an managed object, it works the other way around: You need a managed object context first, and then you can create objects in this context.
See e.g. the NSManagedObjectContext documentation:
An instance of NSManagedObjectContext represents a single “object
space” or scratch pad in an application. Its primary responsibility is
to manage a collection of managed objects. These objects form a group
of related model objects that represent an internally consistent view
of one or more persistent stores. A single managed object instance exists in one and only one context, but multiple copies of an object
can exist in different contexts.
and the NSManagedObject documentation:
If you instantiate a managed object directly, you must call the designated initializer
(initWithEntity:insertIntoManagedObjectContext:).
There is also an utility method insertNewObjectForEntityForName:inManagedObjectContext: to create new objects. For example:
Collect *collect = [NSEntityDescription insertNewObjectForEntityForName:#"Collect"
inManagedObjectContext:[MyAppDelegate managedObjectContext]];
(You could put that into a class method of Collect if you like.)
And note that NSManagedObject already has a managedObjectContext method to get the context of the object:
NSManagedObjectContect *context = [collect managedObjectContext];
It is therefore not necessary (or might even cause problems) if you add an instance variable managedObjectContext to your managed object class.

Custom NSManagedObject classes to add/remove Object from NSOrderedSet

I'm learning Cocoa and following an example in Aaron Hillegass's book (Chapter 32 Core Data Relationships) and can't get it working for my own app.
I have a core data model that consists of a parent object with a to-many relationship to children but mine is an ordered set unlike in the book which is a basic NSMutableSet.
Each of the objects, parent and child, have associated NSArrayControllers and I've developed a document-based app in the Interface Builder with buttons and bindings that add/remove child objects (called perceptrons in my case) from the selected parent (called a layer). That's all working.
Now I want to intercept the add and remove methods so that I can do my own coding things whenever a child is added or removed.
In the Hillegass book he does this by creating the NSManagedObjects subclasses and then implementing addEmployeesObject: and removeEmployeesObject: methods in the code. So that's what I tried.
Here is my child subclass created by the XCode Editor:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Network, Perceptron;
#interface Layer : NSManagedObject
#property (nonatomic, retain) Network *network;
#property (nonatomic, retain) NSOrderedSet *perceptrons;
#end
#interface Layer (CoreDataGeneratedAccessors)
- (void)insertObject:(Perceptron *)value inPerceptronsAtIndex:(NSUInteger)idx;
- (void)removeObjectFromPerceptronsAtIndex:(NSUInteger)idx;
- (void)insertPerceptrons:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
- (void)removePerceptronsAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInPerceptronsAtIndex:(NSUInteger)idx withObject:(Perceptron *)value;
- (void)replacePerceptronsAtIndexes:(NSIndexSet *)indexes withPerceptrons:(NSArray *)values;
- (void)addPerceptronsObject:(Perceptron *)value;
- (void)removePerceptronsObject:(Perceptron *)value;
- (void)addPerceptrons:(NSOrderedSet *)values;
- (void)removePerceptrons:(NSOrderedSet *)values;
#end
And here are the two methods I implemented in Layer.m as per the text book:
- (void)addPerceptronsObject:(Perceptron *)value
{
NSLog(#"Network '%#' layer %lu is adding perceptron %lu", [[self network] name], [self indexInNetwork], [value indexInLayer]);
NSSet *s = [NSSet setWithObject:value];
[self willChangeValueForKey:#"perceptrons"
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:s];
[[self primitiveValueForKey:#"perceptrons"] addObject:value];
[self didChangeValueForKey:#"perceptrons"
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:s];
}
- (void)removePerceptronsObject:(Perceptron *)value
{
NSLog(#"Network '%#' layer %lu is removing perceptron %lu", [[self network] name], [self indexInNetwork], [value indexInLayer]);
NSSet *s = [NSSet setWithObject:value];
[self willChangeValueForKey:#"perceptrons"
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:s];
[[self primitiveValueForKey:#"perceptrons"] removeObject:value];
[self didChangeValueForKey:#"perceptrons"
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:s];
}
I put NSLogs in so I'm sure they are not getting called - nothing in the console when I add/remove perceptron objects.
What does the ArrayController do when it gets an add: remove: message? Is it sending addObject/removeObject messages directly to the set? Why in the textbook example does it send a message up to the parent object to remove a child? Why is that not happening here? Is there a way to debug this and find out?
thanks
Bill,
You are overriding methods that are dynamically generated by Core Data. I suspect your static entries in the lookup table are just being over written. These methods are not the same as the #property methods, which expect to be overridden.
In a bigger sense, why do you want to insert your code here? There are better supported methods designed for your implementation when objects are instantiated. You may wish to examine -awakeFromFetch and -awakeFromInsert.
Andrew

Core Data and #dynamic

I'm new with Core Data and there are some issues that I don't understand yet.
I have a entity called GCS (a subclass of NSManagedObject for Core Data):
#implementation GCS
#dynamic eye;
#dynamic ...
#dynamic ...
It works fine with Core Data when I do this:
GCS *failedBankDetails = [NSEntityDescription
insertNewObjectForEntityForName:#"GCS"
inManagedObjectContext:context];
failedBankDetails.eye = [NSNumber numberWithInt:12];
But then, in another class I have a property of GCS type:
#interface ModelManager : NSObject
{
GCS *tempGCS;
}
#property (nonatomic, retain) GCS *tempGCS;
...
In a method of ModelManager I tried this:
tempGCS.eye = [NSNumber numberWithInt:0];
But raised exceptions:
Failed to call designated initializer on NSManagedObject class 'GCS'
-[GCS setEye:]: unrecognized selector sent to instance 0x4d32ac0
Why cant I use the dot notation now? I think the #dynamic is the clue, but I shouldn't change it because I need to use it for Core Data, right?
Please help me and sorry for my english.
Thanks!
Dot notation has nothing to do with it, you'd get the same error if you called [tempGCS setEye:...]. The error is that you neglected to call initWithEntity:insertIntoManagedObjectContext: when creating the object in tempGCS; in particular, [[GCS alloc] init] will not work correctly.

Transient attribute not found in entity

I'm trying to get the first letter of an attribute (autor) from my pre populated sqlite database. I have no problems retriving data from the database using coredata. But when I try to get data from my transient property, I get this error message: "NSFetchedResultsController ERROR: object { Autor = "\U00cdtalo Calvino"; } returned nil value for section name key path 'FirstLetter'. Object will be placed in unnamed section"...
I have created a transient attribute called FirstLetter and inside my entity Cit. I have also my class for the entity defined.
Cit.h
#interface Cit : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString * Autor;
#property (nonatomic, retain) NSString * FirstLetter;
- (NSString *) FirstLetter;
#end
Cit.m
#import "Cit.h"
#implementation Cit
#dynamic Autor;
#dynamic FirstLetter;
- (NSString *) FirstLetter {
NSLog(#"doing");
[self willAccessValueForKey:#"FirstLetter"];
NSString * initial = [[self valueForKey:#"Autor"] substringToIndex:1];
[self didAccessValueForKey:#"FirstLetter"];
return initial;
}
#end
I cannot get the it to work. Does anyone have a solution for that??? thanks!
I did something similar in my model class, but I didn't bother creating a transient object in the model. I just added a - (NSString *)sectionIndex method to my model that returned the first letter of the string, same as you.
So, I'd try deleting the transient attribute from your model and see if that works any better.

Resources