Error when adding object to Context in seperate Controller - objective-c

I have a simple app with AppDelegate and MainController - I have passed the managedObjectContext to the MainController (I think successfully) but I receive an error when added an object to the context.
Code:
#implementation AppDelegate
-(void)applicationDidFinishLaunching:(NSNotification *) aNotification
{
// this line is wrong: MainController *controller = [[MainController alloc] init];
controller.managedObjectContext = self.managedObjectContext;
}
#interface MainController : NSObject
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#implementation MainController
-(IBAction)addItem:(id)sender {
NSManagedObject *newObject = [NSEntityDescription
insertNewObjectForEntityForName:#"Person"
inManagedObjectContext: self.managedObjectContext];
//The above line gives an error
ERROR:
+entityForName: nil is not a legal NSManagedObjectContext parameter search for entity name "Person"
If I change the code and do everything in the AppDelegate, everything works without issues.
I am not sure what is going on.
[EDIT]
I needed to create an IBOutlet from the MainController object in IB to the AppDelegate - thanks Nofel.

I think it should be like this:
Person *person = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:_managedObjectContext];
[person setName:someNameTextField]; // if you want to set some properties
[_managedObjectContext save:nil]
It depends on how you create your Core-Data model and what you use in you appDelegate.
Best way is to watch some tutorial or Developer Library first.

Related

Can't bind NSArray with NSArrayController

I have an array (_websites) which returns 2 results (i can see the records using NSLog).
What I am trying to do is to display those 2 records in NSTableView that has 3 columns. I make numerous attempts to bind the content of my array with the NSArrayController, without any success.
Here is the .h file
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
#interface CombinedViewController : NSViewController <NSTableViewDataSource>
#property (nonatomic,strong) NSManagedObjectContext *mObjContext;
#property AppDelegate *appDelegate;
#property (strong) IBOutlet NSArrayController *combinedRecordsArrayController;
#property (nonatomic,strong)NSArray *websites;
#property (weak) IBOutlet NSTableView *tableView;
#end
the .m file code:
#import "CombinedViewController.h"
#import "Website.h"
#import "Customer.h"
#import "Hosting.h"
#interface CombinedViewController ()
#end
#implementation CombinedViewController
- (void)viewDidLoad {
[super viewDidLoad];
_appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
self.mObjContext = _appDelegate.managedObjectContext;
[self getCombinedResutls];
}
-(NSArray *)getCombinedResutls {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Website" inManagedObjectContext:self.mObjContext];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [self.mObjContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"Error:%#",error);
}
_websites = [_mObjContext executeFetchRequest:fetchRequest error:nil];
for (Website *ws in _websites) {
Customer *cust = ws.customerInfo;
Hosting *host = ws.hostingInfo;
NSLog(#"Website: %#, Customer: %#, Hosting Provider: %#",ws.websiteUrl, cust.customerName, host.hostingProvider);
}
return fetchedObjects;
}
#end
I am trying to learn how to do it both ways, using cocoa binding and programmatically, so any kind solution will be appreciated. Links with some up to date tutorial will be also very welcomed.
I just forgot to mention...I bind the NSArrayController with ContentArray in Controller Content, and then I bind the NSTableView with the NSArrayController, as well as my Table Column,but I am getting empty NSTableView...no error is shown in console whatsoever.
If you use direct iVar access-- _websites-- to write the websites property's iVar, the KVO notification that the binding depends upon never happens.
If you instead use self.websites = or more explicitly [self setWebsites: ..., then you will trigger a KVO notification to the array controller that the value of the websites property has been updated.
Instead, the array controller in the Xib is unarchived and bound to websites before viewDidLoad, so at that point, websites is nil. And subsequently, you never trigger any KVO notification about websites changing value because you explicitly avoid using the websites accessor setWebsites and instead use direct instance variable access. So the AC never knows that websites changes and the table never reflects any value for websites except nil.
In general never use the instance variable to access a property's value unless you have a very good reason to do so and fully understand why you're doing so.

Unrecognized selector when populating an NSManagedObject

I have an iPad app using Core Data. In my data model I have an object called HubBrand and have generated NSManagedObects using XCode. The generated object has the following code:
Header:
#class HubModel;
#interface HubBrand : NSManagedObject
#property (nonatomic, retain) NSString * brandName;
#property (nonatomic, retain) NSSet *relModels;
#end
#interface HubBrand (CoreDataGeneratedAccessors)
- (void)addRelModelsObject:(HubModel *)value;
- (void)removeRelModelsObject:(HubModel *)value;
- (void)addRelModels:(NSSet *)values;
- (void)removeRelModels:(NSSet *)values;
#end
Implementation:
#implementation HubBrand
#dynamic brandName;
#dynamic relModels;
#end
I am trying to create an instance of the HubBrand class and populate it using the foloowing code:
HubBrand *brand = [[HubBrand alloc] init];
[brand setBrandName:[NSString stringWithFormat:#"_Custom:, %#", [_txtHubBrand text]]];
//brand.brandName = [NSString stringWithFormat:#"_Custom:, %#", [_txtHubBrand text]];
When I do so, I get the following runtime error:
-[HubBrand setBrandName:]: unrecognized selector sent to instance
When using generated managed objects, am I required to implement my own setters? Any clues as to why I am getting this error? Thanks!
You need to create an instance of a sub class of a NSManagedObject using a NSManagedObjectContext based on its NSEntityDescription:
NSManagedObjectContext *managedObjectContext; // Get this from your Core Data stack, probably in the app delegate
HubBrand *brand = [NSEntityDescription insertNewObjectForEntityForName:#"HubBrand" inManagedObjectContext:managedObjectContext];
[brand setBrandName:[NSString stringWithFormat:#"_Custom:, %#", [_txtHubBrand text]]];
See the Creating, Initializing, and Saving a Managed Object section of the doc for more info.
You can also use the sub classes initializer:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"HubBrand" inManagedObjectContext:managedObjectContext];
HubBrand *brand = [[HubBrand alloc] initWithEntity:entity insertIntoManagedObjectContext:managedObjectContext];
But its a bit more wordy!
You're not calling the designated initializer for NSManagedObject, so you're not getting valid objects. You can't create instances using init, you have to use initWithEntity:insertIntoManagedObjectContext:. It's also possible to use the constructor on NSEntityDescription called insertNewObjectForEntityForName:inManagedObjectContext:.

Crash on NSMutableDictionary's setObject function

Here is my code:
NSLog(#"dictionaryPlayers=%#,%d",[dictionaryPlayers description],dictionaryPlayers.count);
[dictionaryPlayers setObject:#"test" forKey:#"test2"];
dictionaryPlayers is inited in this class's init function:
-(id)init{
...
dictionaryPlayers = [[NSMutableDictionary dictionaryWithCapacity:10]retain];
...
}
The program crashed:
Thread 1:Program received signal: "SIGABRT".
And in console:
2011-12-27 17:01:21.744 [25454:207] dictionaryPlayers={
},0
2011-12-27 17:01:21.745 [25454:207] -[__NSCFConstantString tick]: unrecognized selector sent to instance 0x199bcc
With the NSLog outputs, i think dictionaryPlayers is well inited. So I don't know why crashed...
The object on which you call tick: is not longer in memory and causes this crash. Try to see why this object is released.
It seems that this is not a local case, so did you make sure to synthesize it at the top? And declared it correctly in the header? Examples:
In header:
#property (nonatomic, strong) NSMutableDictionary *dictionaryPlayers;
in class:
#synthesize dictionaryPlayers = _dictionaryPlayers;
I removed
dictionaryPlayers = [NSMutableDictionary dictionaryWithCapacity:10];
from init(), and added
dictionaryPlayers = [[NSMutableDictionary alloc] init];
above my log statement. Still crash....
Then I removed
[dicTest setValue:#"Test" forKey:#"testKey"];
So there's only 2 lines left:
dictionaryPlayers = [[NSMutableDictionary alloc] init];
NSLog(#"dictionaryPlayers=%#,%d",[dictionaryPlayers description],dictionaryPlayers.count);
It didn't crash. So, it seems the setValue line really is the problem.
You don't need to call retain on the object in your init statement. Also just for giggles try:
dictionaryPlayers = [[NSMutableDictionary alloc] init];
instead of
dictionaryPlayers = [NSMutableDictionary dictionaryWithCapacity:10];
And do this right above your log statement (take it out of the init).
If this works, put a log in your init method and make sure that is being called before your method that adds KV's to the dictionary
I cannot reproduce this behavior. Here is my code:
ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property(nonatomic, retain) NSMutableDictionary *dictionaryPlayers;
#property (retain, nonatomic) IBOutlet UITextView *logTextView;
- (IBAction)logButtonPressed:(id)sender;
#end
ViewController.m:
#import "ViewController.h"
#implementation ViewController
#synthesize dictionaryPlayers;
#synthesize logTextView;
#pragma mark - My Methods
- (IBAction)logButtonPressed:(id)sender {
logTextView.text = [NSString stringWithFormat:#"%#,%d",[dictionaryPlayers description],[dictionaryPlayers count]];
NSLog(#"dictionaryPlayers=%#,%d",[dictionaryPlayers description],[dictionaryPlayers count]);
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
dictionaryPlayers = [[NSMutableDictionary alloc] init];
[dictionaryPlayers setValue:#"Test" forKey:#"testKey"];
NSLog(#"dictionaryPlayers=%#,%d",[dictionaryPlayers description],[dictionaryPlayers count]);
}
No issues. If you are doing things this way, you cannot have problems. Here are the steps I would take to troubleshoot further:
Do a project search for playersDictionary and make sure there isn't something else touching that object.
Try cleaning the project
Create a new project with this structure and see what happens

After setting one NSManaged object to another it returns Null

I am trying to pass the selected object in my coredata from the rootviewcontroller to the edit view. The selected object is being passed but is then becoming null after the theObject=selectedObject is being called. Anyone know what im doing wrong?
This is in the edit.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "LearningAppDelegate.h"
#interface edit : UIViewController <UITextViewDelegate, UITableViewDelegate, UITableViewDataSource, UIActionSheetDelegate>{
UITableView *tableView;
NSManagedObject *theObject;
UITextView *messageView;
}
#property(nonatomic, retain) IBOutlet UITableView *tableView;
#property(nonatomic, retain) IBOutlet UITextView *messageView;
#property(nonatomic, retain) NSManagedObject *theObject;
-(id)initWithObject:(NSManagedObject *)selectedObject;
#end
This is in the edit.m:
-(id)initWithObject:(NSManagedObject *)selectedObject {
self = [super init];
if (nil == self) {
return nil;
}
NSLog(#"selectedObject: %#", selectedObject);
NSLog(#"selecetedObject.message: %#", [[selectedObject valueForKey:#"message"] description]);
theObject=selectedObject;
NSLog(#"theObject 1: %#", theObject);
NSLog(#"theObject.message 1: %#", [[theObject valueForKey:#"message"] description]);
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSLog(#"theObject 2: %#", theObject);
NSLog(#"theObject.message 2: %#", [[theObject valueForKey:#"message"] description]);
messageView.text=[[theObject valueForKey:#"message"] description];
[super viewDidLoad];
}
I am actually amazed that doesn't crash for you. You're assigning the variable selectedObject into the instance variable theObject without retaining it for your own use. By accessing the instance variable directly in the assignment 'theObject=selectedObject', you're bypassing the behavior granted by the #property declaration. This means that once selectedObject is finally dealloc'd, theObject will point to garbage memory.
The correct way to do this is to put theObject = [selectedObject retain]; in the -initWithObject: method and in -viewDidLoad to access it via self.theObject rather than just theObject.
In my own usage I prefer to give instance variables names different from the actual property name to avoid confusion. For example:
#interface SomeClass : NSObject
{
#private
NSManagedObject *_theObject;
}
#property (nonatomic, retain) NSManagedObject *theObject;
...
#end
#implementation SomeClass
#synthesize theObject = _theObject
...
- (void)dealloc
{
[_theObject release], _theObject = nil;
[super dealloc];
}
#end
I only have experience with Coredata on the desktop, but the problem looks like it would be with your initWithObject method. At no point do you actually insert the new object into the managed object context. You should be using this method to make new objects:
- (id)initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context
As an example in pseudocode:
NSManagedObject *newObject = [[NSManagedObject alloc] initWithEntity:NSENTITYDESCRIPTION insertIntoManagedObjectContext:MANAGEDOBJECTCONTEXT];
[newObject setValue:#"VALUE_OF_SELECTED_OBJECT" forKey:#"APPROPRIATE_KEY"];
//REPEAT AS NECESSARY
[MANAGEDOBJECTCONTEXT save];
*Code not tested, naming conventions are ignored, etc.
The save is important. If you don't do this the object won't persist.

Pass array from one Objective-C class to another

Im attempting to pass an array that is created in one class into another class. I can access the data but when I run count on it, it just tells me that I have 0 items inside the array.
This is where peopleArray's data is set up, it's in a different class than the code that is provided below.
[self setPeopleArray: mutableFetchResults];
for (NSString *existingItems in peopleArray) {
NSLog(#"Name : %#", [existingItems valueForKey:#"Name"]);
}
[peopleArray retain];
This is how I get the array from another class, but it always prints count = 0
int count = [[dataClass peopleArray] count];
NSLog(#"Number of items : %d", count);
The rest of my code:
data.h
#import <UIKit/UIKit.h>
#import "People.h"
#class rootViewController;
#interface data : UIView <UITextFieldDelegate>{
rootViewController *viewController;
UITextField *firstName;
UITextField *lastName;
UITextField *phone;
UIButton *saveButton;
NSMutableDictionary *savedData;
//Used for Core Data.
NSManagedObjectContext *managedObjectContext;
NSMutableArray *peopleArray;
}
#property (nonatomic, assign) rootViewController *viewController;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) NSMutableArray *peopleArray;
- (id)initWithFrame:(CGRect)frame viewController:(rootViewController *)aController;
- (void)setUpTextFields;
- (void)saveAndReturn:(id)sender;
- (void)fetchRecords;
#end
data.m(some of it at least)
#implementation data
#synthesize viewController, managedObjectContext, peopleArray;
- (void)fetchRecords {
[self setupContext];
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"People" inManagedObjectContext:managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Define how we will sort the records
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Name" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (!mutableFetchResults) {
// Handle the error.
// This is a serious error and should advise the user to restart the application
}
// Save our fetched data to an array
[self setPeopleArray: mutableFetchResults];
for (NSString *existingItems in peopleArray) {
NSLog(#"Name : %#", [existingItems valueForKey:#"Name"]);
}
[peopleArray retain];
[mutableFetchResults release];
[request release];
//NSLog(#"this is an array: %#", eventArray);
}
login.h
#import <UIKit/UIKit.h>
#import "data.h"
#class rootViewController, data;
#interface login : UIView <UITextFieldDelegate>{
rootViewController *viewController;
UIButton *loginButton;
UIButton *newUser;
UITextField *entry;
data *dataClass;
}
#property (nonatomic, assign) rootViewController *viewController;
#property (nonatomic, assign) data *dataClass;
- (id)initWithFrame:(CGRect)frame viewController:(rootViewController *)aController;
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField;
#end
login.m
#import "login.h"
#import "data.h"
#interface login (PrivateMethods)
- (void)setUpFromTheStart;
- (void)loadDataScreen;
-(void)login;
#end
#implementation login
#synthesize viewController, dataClass;
-(void)login{
int count = [[dataClass peopleArray] count];
NSLog(#"Number of items : %d", count);
}
Is it the same object? If so, what you have should work. Check to see how you are getting the dataClass instance -- if you alloc a new one, you don't get the array from the other object.
Edit: From your comments below, it appears that you are having some confusion on the difference between classes and objects. I will try to explain (I'm going to simplify it):
A class is what you write in Xcode. It's the description that lets your application know how to create and access objects at run-time. It is used to figure out how much memory to allocate (based on instance variables) and what messages can be sent, and what code to call when they are. Classes are the blueprints for creating objects at runtime.
An object only exists at run-time. For a single class, many objects of that class can be created. Each is assigned its own memory and they are distinct from each other. If you set a property in one object, other objects don't change. When you send a message to an object, only the one you send it to receives it -- not all objects of the same class.
There are exceptions to this -- for example if you create class properties (with a + instead of a - at the beginning), then they are shared between all objects -- there is only one created in memory, and they all refer to the same one.
Also, since everything declared with a * is a pointer -- you could arrange for all pointer properties to point to the same data. The pointer itself is not shared.
Edit (based on more code): dataClass is nil, [dataClass peopleArray] is therefore nil, and then so is the count message call. You can send messages to nil, and not crash, but you don't get anything useful.
I don't see how the login object is created. When it is, you need to set its dataClass property.
Try running the code in the debugger, setting breakpoints, and looking at variables.
From the code, it looks like you are passing a mutable array.
[self setPeopleArray: mutableFetchResults];
Probably the items of the array are removed somewhere in your calling class / method. Or the array is reset by the class from which you get the mutableFetchResults in the first place.