Saving to database using FMDB crashes on interpreting NSInteger - objective-c

When the below function is called, I get the EXC_BAD_ACCESS crash. It looks like FMDB is having a problem interpreting the subject_id NSInteger as it makes it through the two NStrings and bombs when it hits this subject_id column in the WHERE clause.
- (void) saveAllData {
if(isDirty) {
DrillDownAppAppDelegate *appDelegate = (DrillDownAppAppDelegate *)[[UIApplication sharedApplication] delegate];
FMDatabase *database = [FMDatabase databaseWithPath:appDelegate.getDBPath];
if ([database open]) {
[database executeUpdate:#"update Subject Set subject = ?, category = ? where subject_id = ?",
self.title, self.category_title, self.subject_id];
[database close];
}
isDirty = NO;
}
//Reclaim all memory here.
[title release];
title = nil;
[category_title release];
category_title = nil;
}
The problem is the same as I ran into in another post on FMDB insert problems and this boils down to something wrong with my subject_id member. I believe I am using a wrong declaration in the header. Here it is:
//
// Subject.h
// DrillDownApp
#import <UIKit/UIKit.h>
#interface Subject : NSObject {
NSInteger subject_id;
NSString *category_title;
NSString *title;
// NSMutableArray *quotes;
BOOL isDirty;
// BOOL isDetailViewHydrated;
}
- (id) initWithPrimaryKey:(NSInteger)pk;
#property (nonatomic, readwrite) BOOL isDirty;
//#property (nonatomic, readwrite) BOOL isDetailViewHydrated;
- (void) addSubject;
- (NSInteger)getNextSubjectId;
#property (nonatomic, assign) NSInteger subject_id;
#property (nonatomic, copy) NSString * title;
#property (nonatomic, copy) NSString * category_title;
//#property (nonatomic, retain) NSMutableArray *quotes;
//- (void) saveAllData;
#end
(Note: I edited this majorly as I figured out the rest of it.)

Ok I solved it. FMDB will not work using Integers. You must convert them into Numbers. I found this done in the examples on FMDB doc and there is never an int being passed through an executeUpdate statement.
So in my example above the way I fixed this was with the following:
[database executeUpdate:#"update Subject Set subject = ?, category = ? where subject_id = ?", self.title, self.category_title, [NSNumber numberWithInt:self.subject_id]];
I wished this was better documented, but oh well.

I don't know from where you are calling the method saveAllData. Still your question lacks requirement so anyone to answer.
Also, I have found one problem with your code.
Instead of the following code,
//Update the value.
[sub setTitle:txtSubject.text];
[sub setCategory_title:txtCategory.text];
use the following code
//Update the value.
sub.title = txtSubject.text;
sub.category_title = txtCategory.text;
so that the strings have the copy property. If you override the setter method, then you might need to explicitly copy the string. Else it will just assign the value without copy.

Related

Objective-C class returning (null) for control values?

I am trying to make a TableViewController.. I got it to work using code from a youtube lesson: "Cocoa Programming L13-14" But then when I try to change it so that the default values aren't hard coded... but rather the values of controls in the Interface Builder, I get (null) across the board. Here is the code:
#import <Foundation/Foundation.h>
#interface Person : NSObject {
IBOutlet NSPathControl* pcSource;
IBOutlet NSPathControl* pcDestination;
IBOutlet NSTextField* tfBackupAmount;
NSURL* urlSource;
NSURL* urlDestination;
NSString* strBackupAmount;
//Old--
//NSString* name;
//int age;
}
#property NSURL* urlSource;
#property NSURL* urlDestination;
#property NSString* strBackupAmount;
//Old--
//#property (copy) NSString* name;
//#property int age;
#end
and
#import "Person.h"
#implementation Person
#synthesize urlSource;
#synthesize urlDestination;
#synthesize strBackupAmount;
//Old--
//#synthesize name;
//#synthesize age;
- (id)init {
self = [super init];
if (self) {
urlSource = [pcSource URL];
urlDestination = [pcDestination URL];
strBackupAmount = [tfBackupAmount stringValue];
NSLog(#"%#\n%#\n%#",urlSource,urlDestination,strBackupAmount);
//Old--
//name = #"Yoda";
//age = 900;
//NSLog(#"%#: %i", name, age);
}
return self;
}
#end
Everything commented //Old-- worked, and interacted fine with the TableViewController. So I am assuming all that still works fine. The 3 controls (2 NSPathControl & 1 NSTextField) are linked up to an Object class:Person in Interface Builder with the controls linked up. Why am I getting output of:
(null)
(null)
(null)
? When I get to the NSLog(); line? Where am I going wrong?
Thanks!
pcSource, pcDestination, or tfBackupAmount aren't initialized when your init method is called, so they're all nil. Sending a message to nil is legal in Objective-C, and you'll just get nil back. That means urlSource, urlDestination, and strBackupAmount are all nil too, and that's why you ge the log output you're seeing.
You need to change the log message to sometime after those variables are initialized.
Try putting the code in -viewDidLoad rather than -init. It all has to do with the order of events (-init gets called before any of the IB stuff happens.
Ok, technically this question - I found an answer for it. It is to make a custom init method. In my case this means:
Person* p = [[Person alloc] initWithurlSource:[NSURL URLWithString:#"moo"] andurlDestination:[NSURL URLWithString:#"cow"] andstrBackupAmount:#"foo"];
However this still doesn't solve my problem of getting values for IBOutlets from another Class (in this case my TableViewController class) that has been exposed as #property:
#interface AppDelegate : NSObject <NSApplicationDelegate> {
.....
.....
#property (nonatomic, retain) NSPathControl* pcSource;
#property (nonatomic, retain) NSPathControl* pcDestination;
#property (nonatomic, retain) NSTextField* tfBackupAmount;
I am still having trouble getting values for these controls in my "addButtonPressed" method with:
//ad is AppDelegate - declared in interface as AppDelegate* ad;
NSPathControl* pcSource = [ad pcSource];
NSPathControl* pcDestination = [ad pcDestination];
NSTextField* tfBackupAmount = [ad tfBackupAmount];

Binding NSTextField to NSString

I have a very simple question I hope someone can answer as I start understanding bindings. I want to programmatically change my NSString value and have the NSTextField update to that value thru bindings. I have a NSTextField and NSLabel. To represent the value of the myString properly changing I have a NSButton.
I have NSTextField's Value bound to myString property of App Delegate with Continuously Update Value checked.
I have NSLabel's Value bound to myString Property of App Delegate.
I have NSButton outlet hooked to setDefault method.
When I type in NSTextField the NSLabel updates as expected but when I click the button the myString property is updated but not in the NSTextField.
What do I need to do to get the NSTextField update to the myString property????
AppDelegate.h
#interface AppDelegate : NSObject<NSApplicationDelegate>
{
NSString *myString;
}
#property (assign) IBOutlet NSWindow *window;
#property NSString *myString;
- (IBAction)setDefault:(id)sender;
#end
AppDelegate.m
#implementation AppDelegate
#synthesize window = _window;
#synthesize myString;
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification
{
myString = #"This is a string";
}
- (IBAction)setDefault:(id)sender
{
NSLog(#"%#", myString);
myString = #"This is a string";
NSLog(#"%#", myString);
}
#end
It shouldn't be
myString = #"This is a string";
but this:
self.myString = #"This is a string";
both in -applicationDidFinishLaunching: and in -setDefault:. Don't forget to specify self in your NSLog statements as well. You'd probably like to specify a different string in -setDefault: so that you can actually see that a change is taking place.
One other thing: You're effectively saying that you want to assign to myString, but that's not appropriate for an object. Instead of:
#property NSString *myString;
you should instead use
#property (copy) NSString *myString;
or at least
#property (retain) NSString *myString;
The former is preferred because passing a NSMutableString instance effectively copies it as a NSString, while passing a NSString instance simply retains it.
Good luck to you in your endeavors.
I recommend that you prefix your member variables. This way you can distinguish between setting the member directly or using the setter. In your example I would do the following.
#interface AppDelegate : NSObject<NSApplicationDelegate>
{
NSString *m_myString;
}
#property (assign) IBOutlet NSWindow *window;
#property NSString *myString;
- (IBAction)setDefault:(id)sender;
#end
...
#implementation AppDelegate
#synthesize window = _window;
#synthesize myString = m_myString;
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification
{
self.myString = #"This is a string";
}
- (IBAction)setDefault:(id)sender
{
NSLog(#"%#", m_myString);
self.myString = #"This is a string";
NSLog(#"%#", m_myString);
}
#end
Notice, that I changed the #synthesize to assign the member variable.
To clarify:
self.myString = #"This is a string";
.. is an alternative syntax for ...
[self setMyString:#"This is a string"];
You can also set the member directly ...
[self willChangeValueForKey:#"myString"];
m_myString = #"This is a string";
[self didChangeValueForKey:#"myString"];
But then you need to "inform" observers of the binding as illustrated above.

EXC_BAD_ACCESS when property set on custom class at initialization?

I have a simple Answer class that looks like this:
#interface Answer : NSObject {
NSString *_text;
NSNumber *_votes;
}
#property(nonatomic, retain) NSString *text;
#property(nonatomic, retain) NSNumber *votes;
+(id)initFromAnswerData:(NSSet *)data;
-(id)initWithText:(NSString *)answer;
#end
The implementation looks like this:
#import "Answer.h"
#import "AnswerData.h"
#import "AppDelegate.h"
#implementation Answer
#synthesize text = _text;
#synthesize votes = _votes;
-(id)initWithText:(NSString *)answer {
if( (self=[super init])) {
_text = answer;
_votes = 0;
}
return self;
}
#end
If I create an array of Answers in a view controller using the initWithText: method I inevitably have EXC_BAD_ACCESS errors when I take an Answer in the array and try to find it's text value.
However if I initialize a new Answer, set the text value and then add it to the array I don't have this access issue.
So this causes problems down the line:
[arrayOfAnswers addObject:[[Answer alloc] initWithText:#"Hello"]];
But this doesn't:
Answer *newAnswer = [[Answer alloc] initWithText:nil];
newAnswer.text = #"Hello";
[arrayOfAnswers addObject:newAnswer];
Can anyone explain why?
You're using the attribute _text and _votes directly but not their setters.
So, you're not retaining the input parameter answer for the line
_text = answer;
You should either change to
_text = [answer retain];
or
self.text = answer;
Are you retaining the array that you put your Answers into? That would be my guess at what's wrong.
as you see , your
#property(nonatomic, retain) NSString *text; // this property is retain.
so the setter method of this should be- (void) setText:(NSString*)text{
[_text release];
_text = text;
[_text retain];
so when you call newAnswer.text = #"hello" ,it works, newAnswer holds the text.
but in your initWithText, there's no retain symbol, so sucks.

What does this warning mean in Xcode?

I have a resource which is fetched from a JSON API.
The JSON is parsed into a NSDictionary which, in this case is called game.
I'm creating a new instance of my Game class based on the attributes from the JSON.
Game class has a property called userRegistered which is defined as follows:
// in Game.h
#interface
#property (nonatomic, assign) BOOL userRegistered;
// elsewhere in my code I have
Game *newGame = [[Game alloc] init];
newGame.userRegistered = ([game objectForKey:#"user_registered"] > 0);
The "user_registered" key in the dictionary will always be either 1 or 0.
Xcode warns me the I have -
warning: Semantic Issue: Incompatible integer to pointer conversion passing 'int' to parameter of type 'BOOL *' (aka 'signed char *')
Can someone please explain the issue and how I might resolve it?
Update
My full game class is defined as follows:
#import <Foundation/Foundation.h>
#interface Game : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *photoURL;
#property (nonatomic, copy) NSString *gameURL;
#property (nonatomic, assign) BOOL *userRegistered;
#end
// Game.m
#import "Game.h"
#implementation Game
#synthesize name = _name;
#synthesize partnerName = _partnerName;
#synthesize photoURL = _photoURL;
#synthesize gameURL = _gameURL;
#synthesize userRegistered = _userRegistered;
#end
I'm getting the error in one of my ViewControllers in this method
// api_response.body has just been set to an __NSCFArray containing
// NSDictionaries by AFNetworking
NSDictionary *game;
Game *newGame;
for (game in api_response.body){
newGame = [[Game alloc] init];
NSLog(#"Creating a new game");
// set attributes for new game instance
newGame.name = [game objectForKey:#"name"];
newGame.photoURL = [game objectForKey:#"photoURL"];
// user registered is either 0 (false) or 1 (true)
newGame.userRegistered = [[game objectForKey:#"user_registered"] intValue];
// add the game instance to the appropriate array
[self addGameToGamesArray:newGame];
newGame = nil;
}
The warning shows over newGame.userRegistered = [[game objectForKey:#"user_registered"] intValue];
[game objectForKey:#"user_registered"] is likely giving you an NSNumber object. You probably mean instead to compare the integer value inside that NSNumber object.
([[game objectForKey:#"user_registered"] intValue] > 0)
UPDATE in response to your update:
Your problem is with how you're declaring your BOOL property - you have a * that you need to remove.
#property (nonatomic, assign) BOOL *userRegistered;
should be
#property (nonatomic, assign) BOOL userRegistered;
I was able to solve this issue by simply using boolValue
game.userRegistered = [[json objectForKey:#"user_registered"] boolValue];
Thanks all for the help
objectForKey function will return an objective-c instance.
([[game objectForKey:#"user_registered"] boolValue] > 0)
([game boolForKey:#"user_registered"]==YES)

How to access the object

I would like to access the the _news items in a loop. But don't know how to get this done.
My Game.m looks like
#import "Game.h"
#implementation Game
#synthesize homename = _homename;
#synthesize guestname = _guestname;
#synthesize date = _date;
#synthesize gametype = _gametype;
#synthesize news = _news;
#synthesize gameId = _gameId;
-(NSString*)description{
return [NSString stringWithFormat:#"%# gegen %# (%#)", self.homename, self.guestname, self.gametype];
}
#end
My Game.h looks like
#import <Foundation/Foundation.h>
#import <RestKit/RestKit.h>
#import "News.h"
#interface Game : NSObject {
NSString* _homename;
NSString* _guestname;
NSString* _date;
NSString* _gametype;
NSNumber* _gameId;
// News* news;
#public
NSArray* _news;
}
#property (nonatomic, retain) NSString* homename;
#property (nonatomic, retain) NSString* guestname;
#property (nonatomic, retain) NSString* date;
#property (nonatomic, retain) NSString* gametype;
//#property (nonatomic, retain) News* news;
#property (nonatomic, retain) NSArray* news;
#property (nonatomic, retain) NSNumber* gameId;
#end
My News.h looks like
#import <Foundation/Foundation.h>
#import <RestKit/RestKit.h>
#interface News : NSObject {
NSString* _minute;
NSString* _title;
NSString* _bodytext;
NSString* _player;
}
#property (nonatomic, retain) NSString* minute;
#property (nonatomic, retain) NSString* title;
#property (nonatomic, retain) NSString* bodytext;
#property (nonatomic, retain) NSString* player;
#end
My news.m looks like
#import "News.h"
#implementation News
#synthesize player = _player;
#synthesize title = _title;
#synthesize bodytext = _bodytext;
#synthesize minute = _minute;
#end
And the code where I want to access the variable looks like:
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
NSLog(#"Loaded statuses: %#", objects);
HeaderText.text = [NSString stringWithFormat:#"%#", objects ];
// for(News* n in _news) NSLog([n _bodytext]);
// for (id object in objects) {
// NSLog(#"News = %#", object );
// }
}
The NSLog with objects looks good for the game. The next thing is that I want to do something like (above is more pseudo code than real code because I don't know how to do it right):
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
NSLog(#"Loaded statuses: %#", objects);
HeaderText.text = [NSString stringWithFormat:#"%#", objects ];
// loop all
for (id object in objects) {
news = objects.news;
for (id mynews in news) {
NSLog(#"News minute = %#", news.minute );
NSLog(#"News title = %#", news.title );
NSLog(#"News bodytext = %#", news.bodytext );
NSLog(#"News player = %#", news.player );
}
}
}
How to do the getter/setter methods right (so that I can use it here)?
Sorry for the surely stupid question but I don't get the point with it.
Two things:
1stly, is the _news object a private variable, without having a property declaration (getters and setters for e.g.)? The '_variableName' format is usually used to denote private variables.
2ndly, if it is not a private variable, do all the items within the _news array belong to the same class?
If so, you can do a
for (NewsObject *theNewsObject in _news)
{
//code here
}
The for(id randomObject in array) is useful when you don't know what type of object is in the array or if the objects contained in the array are of different types.
Now, again, all the objects inside the NewsObject ought to be public properties that can be accessed by other classes (they should have getters and setters).
Hope this helps. :)
EDIT FOR UPDATED QUESTION
So, if I'm getting your question correctly, you have a Game object, which has an array of News object inside it.
So, in your Game.h
NSString* _homename;
NSString* _guestname;
NSString* _date;
NSString* _gametype;
NSNumber* _gameId;
NSArray * _newsObjects; //declare it as NSMutableArray if you need to mutate it
Now, where you declare your properties;
#property(nonatomic, retain)NSArray *newsObjects
Synthesize it like you normally would in the Game.m file
You are creating the getters/setters automatically by using the #synthesize directive. It creates the getters and setters for you.
Again, from your code, it looks like the NSArray of objects that are passed through the method
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects
consist of Game objects.
So, to access the News object from within the array of Game objects, import News.h and Game.h in the class where this method is being executed, and do the following:
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
NSLog(#"Loaded statuses: %#", objects);
// loop all
for (Game *gameObject in objects) {
NSArray *newsObjectArray = [gameObject newsObjects] //or gameObject.newsObject
for (News *mynews in newsObjectArray) {
NSLog(#"News minute = %#", mynews.minute );
NSLog(#"News title = %#", mynews.title );
NSLog(#"News bodytext = %#", mynews.bodytext );
NSLog(#"News player = %#", mynews.player );
}
}
}
What I found in your code that there was code that was being executed which was not declared in any part of the example that you posted.
What the code above will do is it will look through the Game object array (called objects).
Then, for each gameObject within that array, it will loop through the array of newsObject for that gameObject.
Hope this helps. :)