[NSManagedObject sayHello]: unrecognized selector sent to instance 0x - objective-c

I try to extend NSManagedObject.
Using XCode I created MyBox.m and MyBox.h (directly from the xcdatamodel file).
Then I modified these files:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface MyBox : NSManagedObject
#property (nonatomic, retain) NSDate * endDate;
#property (nonatomic, retain) NSNumber * globalId;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSDate * startDate;
-(NSString *)sayHello;
#end
and
#import "MyBox.h"
#implementation MyBox
#dynamic endDate;
#dynamic globalId;
#dynamic name;
#dynamic startDate;
-(NSString *)sayHello {
return #"hello";
}
#end
I can fetch all myBoxes
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"MyBox" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSMutableArray *myBoxes = [context executeFetchRequest:fetchRequest error:&error];
but later I call
MyBox *myBox = [myBoxes objectAtIndex:indexPath.row];
[myBox sayHello];
it compiles but then I get
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObject sayHello]: unrecognized selector sent to instance 0x8e73fc0'
If I only read a value like
NSLog(#"%#", myBox.name);
it works
I found similar problems here, but no solution.
Thanks for your help.

I've just got the same issue. Solved it by changing class name to the name of my NSManagedObject subclass in myApp.xcdatamodeld -> configurations -> default -> entities -> myEntity.

Assuming you have set the class name correctly on the MyBox entity, I would guess that the app has an older version of your Core Data managed object model. Clean your build and delete the app on the simulator/device for good measure. To be 100% sure, also delete your derived data folder.
If it doesn't work after that, I'll bet that you haven't set the entity class name correctly. Print out your NSEntityDescription and make sure it is what you expect.

For Swift 5.0
This issue is present when you create the CoreData object in this way:
let object = CoreDataClass()
print(object.someProperty) // this is emit crash

I had the right class name set in xcdatamodeld, but I didn't include the class's .m file in my target. I had to click on the .m on the left sidebar, then check the correct box on the right sidebar under Target Membership.

Wrong Xcdatamodel.
I had the wrong xcdatamodel. It's a super dumb mistake but when you assume the latest model is 27 but your coworker changed it to 28 and you added your properties to model 27, it happens.You get these kind of errors and you assume it's something wrong with your Core Data model but it's simply your xcdatamodel number.
Gotta love programming =_=.

Related

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:.

UISearchBar, NSPredicate, w/ Array of Objects

I have experience with Java and Android development, and am now attempting to learn Objective-C and iPhone/iPad development. In order to help teach myself, I am re-writing an application I made for android over to iPhone.
I am now attempting to use the UISearchBar in a tableview that I have populated with names from "member" objects. However, I am having trouble using NSPredicate to retrieve the name properties from inside the member objects that I have created, as it crashes. I was able to create a workaround by making an entirely seperate array filled with just the names and use that with NSPredicate, but this is far from ideal and creates problems down the road.
So basically by doing this I was able to pinpoint the problem to either how I use NSPedicate or maybe how I set my member objects in a previous class. Just to clarify, my object is properly filled when I do go into the method that uses NSPredicate so I know my objects are not just nil.
Here is my .h for my member class.
#interface AKPsiMember : NSObject
#define CURRENT_STATUS #"Current"
#define ALUMNI_STATUS #"Alumni"
#property (nonatomic, strong) NSString *firstName;
#property (nonatomic, strong) NSString *lastName;
#property (nonatomic, strong) NSString *emailAddress;
#property (nonatomic, strong) NSString *pledgeClass;
#property (nonatomic, strong) NSString *major;
#property (nonatomic, strong) NSString *phoneNum;
And also my TableViewController .m that contains the UISearchBar method delegates
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF.firstName contains[c] %#",
searchText];
self.searchedMemberNameResults = [self.listedMembers filteredArrayUsingPredicate:resultPredicate];
}
And finally the error from my stack trace
'NSInvalidArgumentException', reason: '-[AKPsiMember isEqualToString:]: unrecognized selector sent to instance 0x716bbd0'
Well I feel silly. My problem was actually setting my cell.textLabel.text = to the member object instead of the string in the object. Apparently I made this change by accident some time ago when first implementing the UISearchBar and just never noticed.
My predicate is actually working correctly now! Thanks for everyone that took the time to help me out.

Deleting Core Data objects from NSMutableArray

I've got an NSMutableArray that currently holds a bunch of Core Data entities like so:
NSFetchRequest *fetchReq = [[NSFetchRequest alloc]init];
[fetchReq setEntity:[NSEntityDescription entityForName:#"Subject"
inManagedObjectContext:self.managedObjectContext]];
subjectsArray = [self.managedObjectContext executeFetchRequest:fetchReq error:nil];
I then put this array in a dictionary:
_childrenDictionary = [NSMutableDictionary new];
[_childrenDictionary setObject:subjectsArray forKey:#"SUBJECTS"];
This dictionary is then used as the data for a Source List I have implemented. What I'm having issues with is deleting an object from the subjectsArray. I've tried this with the following code:
_selectedSubject = [subjectsArray objectAtIndex:0];
[subjectsArray removeObject:subjectToDelete];
Now this works fine, when I execute the following:
for (Subject *s in subjectsArray) {
NSLog(#"Subjects: %#", [s title]);
}
The Subject I selected to delete is no longer there and the Source List I have updates correctly after calling:
[_sidebarOutlineView reloadData];
The problem I am having though is that when I quit the application and open it up again, the Subject I previously deleted is still there.
The fetching of the core data entities into the array and dictionary is done inside applicationDidFinishLaunching. At the moment all this code is inside the AppDelegate file, which has a .h file that looks like this:
#import <Cocoa/Cocoa.h>
#import "Subject.h"
#interface LTAppDelegate : NSObject <NSApplicationDelegate, NSOutlineViewDelegate, NSOutlineViewDataSource, NSMenuDelegate> {
IBOutlet NSWindow *newSubjectSheet;
IBOutlet NSWindow *newNoteSheet;
IBOutlet NSWindow *newEditSheet;
NSMutableArray *subjectsArray;
}
#property Subject *selectedSubject;
#property (assign)IBOutlet NSOutlineView *sidebarOutlineView;
#property NSArray *topLevelItems;
#property NSViewController *currentContentViewController;
#property NSMutableDictionary *childrenDictionary;
#property NSArray *allSubjects;
Any ideas as to what is causing to deleted Subject to reappear?
You are removing the objects from the local array, but not the CoreData store itself.
To remove an object from the Core Data store, you need to call managedObjectContext
deleteObject:theObject on it, and then call managedObjectContent save:&error to persist it.
If you are using table views, I would recommend checking out the NSFetchedResultsController as well.
You need to commit your changes to the NSManagedObjectContext for it to persist.
check out the save:&errorOut method on that class for details.

Unrecognized selector for NSManagedObject base class

I am getting an 'unrecognised selector' exception when calling a base-class method on an instance and can't see what the problem is.
I have an object called Form as follows:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "HPSDbBase.h"
#interface Form : HPSDbBase
#end
The base class for Form looks like this:
#import <CoreData/CoreData.h>
#interface HPSDbBase : NSManagedObject
#property (nonatomic, retain) NSString * id;
#property (nonatomic, retain) NSString * json;
-(id)getJSONElement:(NSString*)key;
#end
I then try using the Form object within a view controller method as follows:
HPSAppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];
NSError* error = nil;
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Form" inManagedObjectContext:appDelegate.managedObjectContext]];
NSArray* arrayOfForms = [appDelegate.managedObjectContext executeFetchRequest:request error:&error];
for (int i=0;i<arrayOfForms.count;i++)
{
Form* dbForm = [arrayOfForms objectAtIndex:i];
NSLog(#"Form.json=%#",dbForm.json); // this works
NSString* wwwww = (Form*)[dbForm getJSONElement:#"test"]; // exception here
}
The exception is:
-[NSManagedObject getJSONElement:]: unrecognized selector sent to instance 0x8290940
Can anyone see what I'm doing wrong?
Thanks a million!
EDIT 1
Here is the implementation for HPSDbBase:
#import "HPSDbBase.h"
#implementation HPSDbBase
#dynamic id;
#dynamic json;
-(id)getJSONElement:(NSString*)key
{
NSData *jsonData = [[self json] dataUsingEncoding:NSUTF8StringEncoding];
NSError *e = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options: NSJSONReadingMutableContainers error: &e];
NSDictionary *jsonDictionary = (NSDictionary *)jsonObject;
id rc = [jsonDictionary objectForKey:key];
return rc;
}
#end
I tracked down the problem.
I had renamed my core-data object. I renamed everything I could see regarding the name of the core-data object, but it was obviously not enough. I deleted the core-data entity, then recreated a brand new one with the right name and everything started working.
I had also received this error after renaming a class.
If you prefer not to delete your classes, I found that I could resolve the error by opening up my "xcdatamodeld" file, and clicking on Configurations -> Default. There, the entity's Class description was still referencing the old name. After correcting it here, the problem was resolved.
I also received this error. It transpired that it was simply that there was a typo in the name of the attribute in the core data configuration of the entity. It was named correctly in the #dynamic statement in the class implementation so didn't raise a flag when compiled but did as soon as accessed. Because I was setting up NSManagedObject sub-Entities I was distracted from looking for the obvious. Just listing this here in case someone else is in the same boat.

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.