Custom NSManagedObject classes to add/remove Object from NSOrderedSet - core-data

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

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.

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.

Core Data question

the problem I am trying to solve in an application that is using Core Data is to be able to hold a calculated value in a NSManagedObject custom ivar. The calculated value that I want to store is in fact an image. I do not want to persist these images; I build them and destroy them throughout the lifetime of the application. I tried along the lines of:
#interface RTStaffImage : NSManagedObject {
UIImage *image;
}
// Custom properties
#property (nonatomic, retain) UIImage *image;
// Managed object properties
#property (nonatomic, retain) NSNumber *imageID;
#property (nonatomic, retain) NSString *imageName;
and custom accessors methods:
- (void)setImage (UIImage*)im;
- (UIImage *)image;
and in the implementation:
#implementation RTStaffImage
#synthesize image;
#dynamic imageID;
#dynamic imageName;
This fails at runtime with unrecognised selector problems:
-[NSManagedObject setImage:]: unrecognized selector sent to instance
The above approach is what Apple (or, at least as far as I see having read the docs) outlines for transient properties so it should work :-(
Any ideas, comments?
- (void)setImage (UIImage*)im;
you are missing a colon between setImage and (UIImage*). This is the correct version:
- (void)setImage:(UIImage*)im;
And where are the implementations of those two methods?
-[NSManagedObject setImage:]: unrecognized selector sent to instance
just curious, I read NSManagedObject there, are you sure you create an instance of RTStaffImage there?
Yeah, you have these backwards:
#synthesize image;
#dynamic imageID;
#dynamic imageName;
You're providing an implementation for setImage and image, so image should be #dynamic, and the others you need synthesized methods for, so use #synthesize for imageID and imageName.
Good point, they should all be #dynamic since you're with CoreData.
2nd attempt: you have set RTStaffImage as the Class name in the Entity, right?
3rd attempt: is RTStaffImage.m actually part of the Target being built?

Resources