Why am I getting an invalid NSString from SQLite for my UILabel? - objective-c

I have a view with a UILabel and a UITableView. I'm using this code to get a string from a database:
-(void)getOneQuestion:(int)flashcardId categoryID:(int)categoryId {
flashCardText=[[NSString alloc] init];
flashCardAnswer=[[NSString alloc] init];
NSString *martialStr=[NSString stringWithFormat:#"%d", flashcardId];
NSString *queryStr=[[NSString alloc] initWithString:#"select flashCardText,flashCardAnswer,flashCardTotalOption from flashcardquestionInfo where flashCardId="];
queryStr=[queryStr stringByAppendingString:martialStr];
NSString *martialStr1=[NSString stringWithFormat:#"%d", categoryId];
NSString *queryStr2=[[NSString alloc] initWithString:#" and categoryId="];
queryStr2=[queryStr2 stringByAppendingString:martialStr1];
queryStr=[queryStr stringByAppendingString:queryStr2];
unsigned int lengthOfString=[queryStr length];
char temp2[lengthOfString +1];
strcpy(temp2, [queryStr cStringUsingEncoding:NSUTF8StringEncoding]);
clsDatabase *clsDatabaseObject = [[clsDatabase alloc] init];
sqlite3_stmt *dataRows = [clsDatabaseObject getDataset:temp2];
while(sqlite3_step(dataRows) == SQLITE_ROW) {
flashCardText =[NSString stringWithUTF8String:(char *)sqlite3_column_text(dataRows,0)];
flashCardAnswer=[NSString stringWithUTF8String:(char *)sqlite3_column_text(dataRows,1)];
flashCardTotalOption=sqlite3_column_int(dataRows,2);
}
sqlite3_reset(dataRows);
sqlite3_finalize(dataRows);
[clsDatabaseObject release];
}
When I click on the table cell, the string value (flashCardAnswer) shows invalid.

Although this code snippet doesn't seem to show where the string value is assigned to the UI element, it would seem that the problem could stem from your use of +[NSString stringWithUTF8String:] inside the while loop. This returns an autoreleased string which you much retain if you want to use it outside the scope of the method. Since those appear to be instance variables that you use in another part of the code to change the UI, you have a few options:
Send a -retain to each of them before exiting the method.
Use +alloc and -initWithUTF8String:.
Use a setter method or property that takes care of the details for you. (Thanks, Chuck!)
As a bonus, I have a few other related suggestions.
You're leaking memory by allocating strings for flashCardText and flashCardAnswer at the start of the method, since you overwrite them in the while loop.
Use -[NSString getCString:maxLength:encoding:] to write the query string into a char* buffer without the strcpy() call, or just use the char* from -cStringUsingEncoding: directly.
There is a lot of potential for simplifying the construction of your query string — definitely investigate NSMutableString. For example...
NSMutableString* query = [[NSMutableString alloc] initWithString:#"select flashCardText,flashCardAnswer,flashCardTotalOption from flashcardquestionInfo"];
[query appendFormat:#" where flashCardId=%d", flashcardId];
[query appendFormat:#" and categoryId=%d", categoryId];
clsDatabase *clsDatabaseObject = [[clsDatabase alloc] init];
sqlite3_stmt *dataRows = [clsDatabaseObject getDataset:[query cStringUsingEncoding:NSUTF8StringEncoding]];

Related

Initialize the empty string in ObjectC?

Someone use the following to initialize the NSstring
NSString *astring = [[NSString alloc] init];
I am wondering why not just use
NSString *atring = nil or NSString *astring = #""
There is no semantic difference between NSString *astring = [[NSString alloc] init]; and NSString *astring = #""; - but NSString *astring = nil; is completely different. The first two produce a reference to an immutable string value, the last indicates the absence of a value.
Whether the various ways of generating an zero-length string produce different objects is entirely an implementation detail. The code:
NSString *a = [[NSString alloc] init];
NSString *b = [NSString new];
NSString *c = #"";
NSString *d = [NSString stringWithString:#""];
NSLog(#"%p, %p, %p, %p, %p", a, b, c, d, #""); // %p = print the value of the reference itself
outputs (the exact values will vary):
0x7fff7100c190, 0x7fff7100c190, 0x1000028d0, 0x1000028d0, 0x1000028d0
showing only 2 zero-length string objects were created - one for #"" and one for alloc/init. As the strings are immutable such sharing is safe, but in general you should not rely on it and try to compare strings using reference comparison (==).
NSString *atring = nil
is different -- it's a nil pointer, not an empty string.
NSString *astring = #""
is almost the same, if you change it to something like
NSString* astring=[#"" retain];
It's one of the things that "don't matter"; he or she simply used one way. Probably for no particular reason at all.
NSString *atring = nil; is simply setting the pointer to nil and does nothing other than ensure that pointer is set to nil;
NSString *astring = #""; is a shorthand literal and is the equivalent of [NSString stringWithString:#""];
On another point I don't know why you would want to initialize a string to nothing if its not mutable since you won't be able to change it later without overriding it.

Memory management, should I be retaining?

If I declare an NSString in my header file as follows:
{
NSString *testString;
}
#property(nonatomic,retain) NSString *testString;
Then synthesize the string in my .m file, what is the correct way to initialise it?
If I don't write:
self.testString = [[NSString alloc] init];
then the it is never initialised, but if I do, then isn't the string being retained twice? Or should I initialise it some other way, such as:
self.testString = [NSString stringWithFormat:#"%#, sampleText];
You are correct, the former will over retain the object.
Use the second form instead.
If you had to use the ivar directly however, you need to retain the object :
testString = [[NSString stringWithFormat:#"%#, sampleText] retain];
self.testString = [NSString stringWithFormat:#"%#, sampleText]; or self.testString = [NSString string]; is correct; self.testString = [[NSString alloc] init]; will cause over-retaining.
Consider using ARC (Automatic Retain Counting) for you project. With ARC the compiler takes care of retain counts so you don't have to, in fact aren't allowed to. There is a refactoring that will convert a current project.
It seems that you declare an variable called testString in your .h and you also create a property that retains.
You can either use this:
self.testString = [NSString string];
or you can use
testString = [[NSString alloc] init];
Defining the variable through the property will cause it to be retained, that's why you declared it as (nonatomic, retain). Defining the variable through the declaration won't take those arguments into consideration. Here's a quick rule of thumb about retaining.
Using your code as a base:
self.testString = [[NSString alloc] init]; // Retain count = 2
self.testString = [NSString string]; // Retain count = 1
testString = [[NSString alloc] init]; // Retain count = 1
testString = [NSString string]; // Not retained at all.
First of all #property (nonatomic, copy) NSString *testString to avoid mutability bugs.
Second - if you want just a string without leaks:
self.testString = [NSString string];
self.testString = [[[NSString alloc] init] autorelease];
self.testString = [NSString stringWithFormat:#"%#", text];
these are all valid options.

problems during object release

I have some problems during when and which object to be release
You can say my knowledge towards this is less
i have following conditions please suggest me the answer accordingly
situation-1
NSMutableString *str=[[NSMutableString alloc]initWithFormat:#"Hello World!"];
NSMutableArray *array=[[NSMutableArray alloc]init];
[array addObject:str];
Now when i tried to release str then usage of array affected in future...and Vice Versa
Tell me can i release both?
situation-2
NSMutableString *str=[[NSMutableString alloc]init];
str=#"Hello World !";
str=[self getData]; //calling a method which returns a string
[str release];
I think i am creating a memory leak here(how to solve it?)
please clear these situations
in the first situation, you'll need to call [str release]; after adding it to the array, like this:
NSMutableString *str = [[NSMutableString alloc] initWithString:#"Hello World!"];
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:str];
[str release];
This way, array holds the only retain call on the string. Once you release array later, you won't have any memory leak issues.
I'm a little confused about the second situation. str here is a pointer. You seem to be assigning three different objects to to same pointer:
NSMutableString *str = [[NSMutableString alloc] init]; //first object
str=#"Hello World !"; //second object
str=[self getData]; //third object
by the time you call [str release], you've created a memory leak because you've lost the first mutable string. The second time you called str =, you lost any way to access that first NSMutableString.
Assuming that you're looking to concatenate all of these (since you chose NSMutableString), you might try this:
NSMutableString *str = [[NSMutableString alloc] init]; //first object
[str appendString:#"Hello World!"];
[str appendString:[self getData]];
[str release];
If the [self getData] method returns an autoreleased string, you'll be fine. If getData returns a retained string (if you used alloc and init), you'll need to assign it to an intermediate pointer and release it after adding it to str.
What is the need of creating the NSMutableString You can directly use NSString for this purpose

stringWithFormat vs. initWithFormat on NSString

I am wondering what differences such as disadvantages and/or advantages there are to declaring an NSString this way:
NSString *noInit = [NSString stringWithFormat:#"lolcatz %d", i];
as opposed to:
NSString *withInit = [[NSString alloc] initWithFormat:#"Hai %d", i];
What was the motivation of putting stringWithFormat instead of just having the initWithFormat way of initializing the string?
stringWithFormat: returns an autoreleased string; initWithFormat: returns a string that must be released by the caller. The former is a so-called "convenience" method that is useful for short-lived strings, so the caller doesn't have to remember to call release.
I actually came across this blog entry on memory optimizations just yesterday. In it, the author gives specific reasons why he chooses to use [[NSString alloc] initWithFormat:#"..."] instead of [NSString stringWithFormat:#"..."]. Specifically, iOS devices may not auto-release the memory pool as soon as you would prefer if you create an autorelease object.
The former version requires that you manually release it, in a construct such as this:
NSString *remainingStr = nil;
if (remaining > 1)
remainingStr = [[NSString alloc] initWithFormat:#"You have %d left to go!", remaining];
else if (remaining == 1)
remainingStr = [[NSString alloc] initWithString:#"You have 1 left to go!"];
else
remainingStr = [[NSString alloc] initWithString:#"You have them all!"];
NSString *msg = [NSString stringWithFormat:#"Level complete! %#", remainingStr];
[remainingStr release];
[self displayMessage:msg];
Here, remainingStr was only needed temporarily, and so to avoid the autorelease (which may happen MUCH later in the program), I explicitly handle the memory as I need it.

about get value from sqlite

Code Sample:
NSString *str= [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectStatement, 1)];
Test *t=[[Test alloc] init];
t.str=[str copy]; // why use "copy" here?
[str release];
It looks to be bad coding, for two reasons. Since str is being discarded, you might as well assign it, rather than a copy, to t.str. Second, and more importantly, setters should take care of retaining or copying data.
Should be:
NSString *str= [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectStatement, 1)];
Test *t=[[Test alloc] init];
t.str=str; // No point copying to release original.