I'm moving my initial steps in the Core Data realm (and I'm quite new to iPhone development, too) and I found a behavior I cannot explain.
I declared a subclass of a NSManagedObject and defined a few properties, some of them of type NSString *, MyObject.h is something like:
#interface MyObject : NSManagedObject {
}
#property (nonatomic, retain) NSString *contentFile;
#property (nonatomic, retain) NSString *contentPath;
#property (nonatomic, retain) NSDate *creationDate;
#property (nonatomic, retain) NSString *name;
#end
Now, if I try to create an object instance and assign a value to the name property, when I try to print back the content of the property it seems it's mangled.
In the AppDelegate, where the whole Core Data stack is defined, I write:
MyObject *newObject = [NSEntityDescription insertNewObjectForEntityForName:#"MyObject"
inManagedObjectContext:self.managedObjectContext];
newObject.name = #"Testing";
NSLog([NSString stringWithFormat:#"Name: %s\n", newObject.name]);
What I get in the output console is
2009-08-24 20:03:55.176 MyApp[15727:20b] Name: ‡}00»
I can't understand if I'm doing something wrong or if I forgot something. Anyone can help, please?
You need to use the %# format specifier in stringWithFormat:, since NSString is an Objective-C object:
NSLog([NSString stringWithFormat:#"Name: %#\n", newObject.name]);
%s is used for C-strings (char*s). For more info look at String Format Specifiers.
The correct format specifier for Objective-C objects is %#, not %s.
You should use %#, not %s. %s is for char* strings. You're passing in an objective-c object.
Related
I having an issue where my application crashes with the following exception:
ABC[1936:c07] * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<_NSObjectID_48_0 0xb63e310> valueForUndefinedKey:]: this class is not key value coding-compliant for the key id.'
The strange issue with this exception is that it does not occur when using iOS5. Please see the code where the exception takes place below:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if ((self.sectionInfoArray == nil) ||
([self.sectionInfoArray count] != [self numberOfSectionsInTableView:self.tableView]))
{
NSMutableArray *infoArray = [[NSMutableArray alloc] init];
for (Tour *tour in self.tours)
{
SectionInfo *sectionInfo = [[SectionInfo alloc] init];
sectionInfo.tour = tour;
sectionInfo.open = NO;
NSLog(#"Tour Details Count %#", [[tour tourDetails] objectAtIndex:0]);
NSNumber *defaultRowHeight = [NSNumber numberWithInteger:DEFAULT_ROW_HEIGHT];
NSInteger countOfQuotations = [[sectionInfo.tour tourDetails] count];
for (NSInteger i = 0; i < countOfQuotations; i++)
{
[sectionInfo insertObject:defaultRowHeight inRowHeightsAtIndex:i];
}
[infoArray addObject:sectionInfo];
}
self.sectionInfoArray = infoArray;
}
}
Would this exception be being caused because due to me having a Fetched Property defined within the class Tour that gets an array of TourDetail classes. Please see the implementation code for both classes below:
#import "Tour.h"
#import "TourDetail.h"
#implementation Tour
#dynamic background_url;
#dynamic id;
#dynamic summary;
#dynamic title;
#dynamic tour_tourdetail;
#dynamic tourDetails;
#end
#import "TourDetail.h"
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class TourDetail;
#interface Tour : NSManagedObject
#property (nonatomic, retain) NSString * background_url;
#property (nonatomic, retain) NSNumber * id;
#property (nonatomic, retain) NSString * summary;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) TourDetail *tour_tourdetail;
#property (nonatomic, retain) NSArray *tourDetails;
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface TourDetail : NSManagedObject
#property (nonatomic, retain) NSString * audiofile;
#property (nonatomic, retain) NSString * detail;
#property (nonatomic, retain) NSNumber * id;
#property (nonatomic, retain) NSNumber * lattitude;
#property (nonatomic, retain) NSNumber * longitude;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSNumber * tour_id;
#property (nonatomic, retain) NSManagedObject *tourdetail_tour;
#end
#implementation TourDetail
#dynamic audiofile;
#dynamic detail;
#dynamic id;
#dynamic lattitude;
#dynamic longitude;
#dynamic title;
#dynamic tour_id;
#dynamic tourdetail_tour;
#end
Any help with this issue would be greatly appreciated. As I am at a loss as to how I can fix this.
Thanks,
Michael
UPDATE:
When I remove the Fetched Property the exception does not occur with iOS6. Please see the predicate I have configured below:
Fetched Property tourDetails Predicate tour_id == $FETCH_SOURCE.id
Can you see anything I doing wrong with the setup of this predicate ? My goal is to use this so as I can return an Array of TourDetail objects for each tour_id that mathces the id column inside the Tour table.
UPDATE:
I have been able to diagnose that the exception is being thrown because of the Predicate as when I call both tables separately there is no exception raised. Can you see any issue with the predicate that I have created ?
Please see code below showing how I am retrieving the objects from the Core Data DB:
- (void)viewDidLoad
{
[super viewDidLoad];
[DrivingToursContent setupStaticData];
self.tableView.sectionHeaderHeight = HEADER_HEIGHT;
_openSectionIndex = NSNotFound;
self.tableView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"custombackground.ptoung"]];
self.managedObjectContext = [[BaseCoreDataController sharedInstance] newManagedObjectContext];
[self loadRecordsFromCoreData];
[self loadRecordsFromCoreDataForTourDetail];
NSLog(#"Tour Detail array count: %d", [self.toursTest count]);
// Do any additional setup after loading the view.
}
- (void)loadRecordsFromCoreData {
[self.managedObjectContext performBlockAndWait:^{
[self.managedObjectContext reset];
NSError *error = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([Tour class])];
[request setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"id" ascending:YES]]];
self.tours = [self.managedObjectContext executeFetchRequest:request error:&error];
}];
}
UPDATE:
The root of the problem is definitely coming from the Predicate I have defined for the Fetched property but can you advise how I should write the predicate to link between the 2 tables. As when I write the predicate tour_id == 0 and directly reference an id I know exists the fetched property works correctly. But when I use $FETCH_SOURCE.id the key value coding exception is thrown. What property do you use to reference the the table you wish to link to ?
Really appreciate all your help with this.
Thanks,
Michael
without seeing your code, you are calling a method on a class that was deprecated in iOS6, hence the crash.
There's nothing in the code you've posted for viewWillAppear that touches Core Data in any obvious way at all, which makes it kind of hard to guess what's going on. Some of those objects might be managed objects, but who knows which ones or how you created them?
However the error message does provide a huge clue:
[<_NSObjectID_48_0 0xb63e310> valueForUndefinedKey:]: this class is not key value coding-compliant for the key id.
The fact that this mentions _NSObjectID_48_0 indicates that at some point you're using NSManagedObjectID instances when you're expecting NSManagedObject instances. NSManagedObjectID does not have a property named id, no matter what the entity looks like, so asking one for its id will produce a "not key value coding compliant" error.
As for why this would be happening, it's still impossible to say, since you haven't posted any code that gives any clues about how you are using Core Data.
It might be something to do with the predicate:
tour_id == $FETCH_SOURCE.id
If you constructed a fetch so that $FETCH_SOURCE is an NSManagedObjectID, this would be a problem. Maybe (and I can only guess) you're using it while asking for a result type of NSManagedObjectIDResultType.
#property (nonatomic, retain) NSNumber * id;
Isn't id a reserved word in Objective-C? Have you tried renaming this property to something else, to see if that is causing any conflicts?
Without digging into your code, NSFetchRequest can set the returned result type,
e.g., NSManagedObjectIDResultType or NSManagedObjectResultType.
NSManagedObjectResultType is wanted, but NSManagedObjectIDResultType is returned.
I have the same issue when I use MagicalRecord, but have no issue when use Xcode generate CoreData source code.
Ok, I have read a lot of posts and resources about this but I am STILL having the same issue. I have a few NSString variables that I need to be class-wide variables used in multiple places throughout the class. I have tried many combinations of settings. First of all, I do have the strings declared in the interface like so:
#interface iCodeViewController : UIViewController <NSXMLParserDelegate> {
NSString *myString;
}
I have also added the property as follows (I have tried with and without the property and synthesizing)
#property (readwrite, retain) NSString *myString;
I have also tried, (nonatomic, retain), (nonatomic, copy), (readwrite, copy).
Then in the .m file:
#synthesize myString;
I have tried:
self.myString = #"whatever";
myString = #"whatever";
I have also tried with and without allocating memory to it by:
myString = [[NSString alloc] init];
What am I missing??
After I have 'supposedly' set the string variable in one method, I try to check it in another with if ([myString isEqualToString:#"blah blah"]) and when I put in a breakpoint and hover over myString it is always showing 'invalid summary.
Thanks!
use below
#property (nonatomic, retain) NSString *myString;
self.myString = [NSString stringWithString:#"whatever"];
for more read the SO post
Invalid Summary in NSString assignment
Can you place the class code here? The way you are handling your myString is perfectly fine.
One possibility I can think of is that you are forgetting to return self from the init method.
There could be other possible memory related mess some where in your code.
I was able to reproduce the invalid summary by initializing an NSMutableString to size 100 and only appending to it. I found the problem went away when I called
[mutableString setString:#""];
prior to
[mutableString appendString:string];
I am working an application in which data is being populated from an sqlite database. All database related stuff is done in the appdelegate class. I have used NSMutable array to hold objects.
I have used a separate NSObject class for properties.
I am getting the error: property 'x' with 'retain' attribute must be of object type.
My appdelegate.m file's code is as:
NSString *amovieName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 0)];
NSInteger amovieId = sqlite3_column_int(compiledStatement, 1);
//problem is here the
//value of movieId is coming from database.
//But error: "must be of object type" is puzzling me.
//I am assuming to use NSNumber here.
my NSObject file's code is as:
in .h file-
NSInteger movieId;
its property as:
#property (nonatomic, retain) NSInteger movieId;
and in .m file-
#synthesize movieId;
then I have just initialize as:
-(id)initWithmovieName:(NSString *)mN movieId:(NSInteger)mId
{
self.movieName=mN;
self.movieId=mId;
return self;
}
I found another way as:
assigning value in a NSnumber object .then type caste in NSInteger.for ex;
NSNumber aNSNumbermovieID = sqlite3_column_int(compiledStatement, 1);
NSInteger amovieId = [aNSNumbermovieID integerValue];
but still I am getting the same errors(property 'x' with 'retain' attribute must be of object type).
Any suggestion?
NSInteger is a scalar and not an object. So you shouldn't retain it, it should be assigned. Changing your property will clear up the warning message. You don't need to do the NSNumber stuff that you added in.
#property (nonatomic, assign) NSInteger movieId;
It's a little confusing since NSInteger sounds like a class, but it's just a typedef for int. Apple introduced it as part of 64-bit support so that they can typedef it to the appropriately sized integer for the processor the code is being compiled for.
Just use NSNumber and you can do:
#property (nonatomic, retain) NSNumber *movieId;
I think the error is because of the #property retain for NSInteger. Assign is for primitive values like BOOL, NSInteger or double. For objects use retain or copy, depending on if you want to keep a reference to the original object or make a copy of it.
Here NSInteger is clearly not an object so you should try assign instead of retain
I am trying to parse some JSON for my app.
When trying to set the "name" value I get the error unrecognized selector sent to instance'.
This is the line of code the error happens on: self.name = [json_object objectForKey:#"name"]; (json_object is an NSDictionary and "self" is an object in CoreData)
My JSON looks like this:
json_object: {
draftstatus = "isdraftfull.php";
getcard = "getcard.php";
getpack = "getpack.php";
imageextension = ".jpg";
images = "images/small/";
joindraft = "joindraft.php";
joindraftmenu = "getjoindraftmenu.php";
login = "loginasuser.php";
name = "the server";
passpack = "passpack.php";
playerdraftstatus = "getplayerdraftstatus.php";
signup = "signup.php";
userexists = "checkifuserexists.php";
usesetidforimagepath = false;
}
Server.h looks like this:
#import <CoreData/CoreData.h>
#import "JSON.h"
#interface Server : NSManagedObject
{
NSMutableData *responseData;
}
#property (nonatomic, retain) NSString * playerdraftstatus;
#property (nonatomic, retain) NSString * signup;
#property (nonatomic, retain) NSNumber * usesetidforimagepath;
#property (nonatomic, retain) NSString * login;
#property (nonatomic, retain) NSString * imageextension;
#property (nonatomic, retain) NSString * userexists;
#property (nonatomic, retain) NSString * getcard;
#property (nonatomic, retain) NSString * passpack;
#property (nonatomic, retain) NSString * draftstatus;
#property (nonatomic, retain) NSString * joindraftmenu;
#property (nonatomic, retain) NSString * getpack;
#property (nonatomic, retain) NSString * joindraft;
#property (nonatomic, retain) NSString * images;
#property (nonatomic, retain) NSNumber * isdefaultserver;
#property (nonatomic, retain) NSString * root;
#property (nonatomic, retain) NSString * name;
- (id) initWithWebAddress:(NSString *) address;
- (id) initWithWebAddressAsyn:(NSString *) address;
- (void) fillFromJSON:(NSDictionary *) json_object;
- (void) saveServer;
#end
I believe it was working a few days ago, but I don't remember. When I started working on it today I added the "root" object. It compiles with no errors but won't parse that json.
Help?
EDIT
I changed the JSON to come back with name = "the server" and when I log that like this NSLog(#"json_object name: %#", [json_object objectForKey:#"name"]); the log says json_object name: the server but I still get the same error when I try to set that to self.name which is of type String.
I did notice that sometimes when examining the Log version of the JSON that the value inside name isn't always surrounded in quotes and as you see from the log of [json_object objectForKey:#"name"] it is showing without quotes around it. Is that the problem and why is that happening? If I look at the json straight on the web page that outputs it, there is ALWAYS quotes around the value.
EDIT 2
The full error is as follows:
-[Server setName:]: unrecognized selector sent to instance 0x61036e0
2010-11-19 15:46:10.113 TCGDraft[57953:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Server setName:]: unrecognized selector sent to instance 0x61036e0'
*** Call stack at first throw:
EDIT 3
I did an NSLog on "self" and this is what comes back self: <Server: 0x6143190> (entity (null); id: (null) ; data: {})
I have no clue if that is what is normal though. This is a CoreData object and I have a feeling I might have messed it up somehow.
EDIT 4
I went back and looked at the code to create a Server Object from CoreData like this:
Server *server = [NSEntityDescription
insertNewObjectForEntityForName:#"Server"
inManagedObjectContext:context];
When I ran that I now get an error that just says EXC_BAD_ACCESS so it does look like somehow that object is messed up...but I don't know how.
EDIT 5
Right, so I completely deleted the Server.h and Server.m files with my custom code, I recreated them from the CoreData object and tried that code I posted in "Edit 4" and I STILL get the same error about EXC_BAD_ACCESS....so now I have apparently gone BACKWARD rather than Forward.
Firstly, I suggest you separate the NSManagedObject from your JSON parser methods completely. All the async loading and processing of your JSON data should be dealt with in a separate object that you can then assign to your managed object properties.
#import <CoreData/CoreData.h>
#interface Server : NSManagedObject
{
}
#property (nonatomic, retain) NSString * playerdraftstatus;
#property (nonatomic, retain) NSString * signup;
...
#property (nonatomic, retain) NSString * name;
#end
Secondly, DO NOT change the #dynamic values back to #synthesize otherwise CoreData will not be able to manipulate and save these objects for you (that's most likely why you are getting the EXEC_BAD_ACCESS exception when you try and save the object!).
#dynamic *playerdraftstatus;
#dynamic *signup;
...
#dynamic *name;
Now try assigning the attribute values from the controller where you are creating your Server object i.e.
Server *server = [NSEntityDescription
insertNewObjectForEntityForName:#"Server"
inManagedObjectContext:context];
server.name = [json_object valueForKey:#"name"];
And finally, make sure you don't ever release the server object as CoreData will look after this for you automatically.
PS: if you changed your data model you may have to reset/delete the app from the iPhone/Simulator otherwise you will get an error message saying the store type is incompatible with the existing one on the phone etc.
Let me know how you go and if you get any further error messages.
Cheers,
Rog
The problem is that you don't have a setName method in your Server class. It has nothing to do with the json data. Make sure you synthesize your properties, or otherwise define the requisite methods.
I am currently developing an app that relies heavily on retrieving web content. I have a series of NSObject classes that represent the content that I retrieve in JSON format. I also have NSManagedObject classes that represent my Core Data model that are almost identical.
Here is an example of an NSObject class that I use to hold my web content:
#interface MovieRecord : NSObject {
NSString *movieTitle;
NSDecimalNumber *movieId;
NSString *movieRating;
NSString *movieDescription;
NSDate *movieReleaseDate;
NSMutableArray *movieVideos; // collection of class videoRecord
NSMutableArray *actors;
UIImage *movieImage;
NSURL *movieImageURL;
}
And here is an example of my NSManagedObject class:
#interface Movie : NSManagedObject
{
}
#property (nonatomic, retain) NSNumber * id;
#property (nonatomic, retain) NSString * description;
#property (nonatomic, retain) id image;
#property (nonatomic, retain) NSString * rating;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSDate * releaseDate;
#property (nonatomic, retain) NSString * imageURL;
#property (nonatomic, retain) NSSet* actors;
#end
In this example a user will look through a lot of movies but they will not always be saving the movie to the persistent store. That was my main reason for storing the information into seperate classes first, then if they decide to save it, I will populate the NSManaged object classes and save. The NSObject class will not be fully populated until a user has drilled down to the detail view (initially only movieTitle and movieID will be set).
I guess my question here is does it makes sense to keep these classes separate? Is there a better design approach to this that I am just not seeing? Should I just stick to using the NSDictionary to populate my table views (NSDictionary is populated from the JSON data)?
I am not sure if you got to the bottom of this but I'd suggest you use a NSManagedObject for all your objects and only persist the ones you're interested in.
Remember all your objects are kept in the context and not persisted until you explicitly save the context. Before that you can get rid of unwanted stuff and then sent the context a save message.
You don't have to insert managed objects into a context, as long as you initialize them with the correct information from your managed object model. Assuming that you can get your managed object model from the app delegate, you can do something like the following:
NSManagedObjectModel *objectModel = [appDelegate managedObjectModel];
Movie *obj = [[Movie alloc]
initWithEntity:[[objectModel entitiesByName] objectForKey:#"Movie"]
insertIntoManagedObjectContext:nil
];