Memory management, should I be retaining? - objective-c

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.

Related

Why is this string immutable?

Why does the code give the error - Attempt to mutate immutable object with appendFormat: ?
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
for (NSTextTestingResult *match in matches) {
<omitted>
NSMutableString *value;
value = (NSMutableString *)[response stringWithRange:range];
if ([dict objectForKey:#"traveler"])
[dict objectForKey:#"traveler"] appendFormat:#"%#", value]; // Errors here
[dict setObject:value forKey:key];
}
Value is being created as a _NSCFString.
Because [response stringWithRange:range] returns an immutable NSString *, and casting doesn't make it become mutable.
You want value = [[response stringWithRange:range] mutableCopy];.
Note that if you're not using ARC, you need to remember to release the mutableCopy. Although the return value of [response stringWithRange:range] is autoreleased, the mutableCopy is not.
I dont think you can cast a string to mutable like that.
You need to do it like this
ms = [[NSMutableString alloc] init];
[ms setString:immutableString];
Oops wrong again the way the subclass works you should be able to do it like this more simply.
ms = [NSMutableString stringWithString: immutableString];

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.

Adding multiple NSObjects to an NSMutableArray

With the following code, both items in the array are the same (the last item). What am I doing wrong that is causing this array to overwrite the values? I'm trying to use 1 object so I don't have to instantiate X number of objects.
self.myArray = [[NSMutableArray alloc] init];
MyObjClass *obj = [[MyObjClass alloc] init];
obj.firstName = #"First Name";
obj.lastName = #"Last Name";
obj.created = #"Dec 17 16:24";
[self.myArray addObject:obj];
obj.firstName = #"First Name2";
obj.lastName = #"Last Name2";
obj.created = #"Dec 18 7:41";
[self.myArray addObject:obj];
In MyObjClass.h I have #interface MyObjClass : NSObject. Is NSObject the wrong datatype?
Properties in MyObjClass.h:
#property (strong) NSString *firstName;
#property (strong) NSString *lastName;
And from MyObjClass.m:
#synthesize firstName, lastName;
The array isn't overwriting the values, you are in your code. You have one instance of the MyObjClass. *obj is a pointer to that objects and when you add it to the array twice, the array has two indexes that point to the object you added twice.
By setting the properties on obj, you're changing the values of that one object that both your *obj pointer and the array points to.
Why are you concerned about instantiating X objects? It sound like you want X objects which are in the array with distinct values.
Besides creating X objects, you can copy the first object, set the values then add that to the array but since you're setting all the values anyways, I'm not sure why you just wouldn't init a new object.
EDIT:
Based on your comment below, it looks like your concern of multiple objects is around memory management. When you add an object to an array, it retains the object so after you add it (if you're done with it in that scope), then release or autorelease it. When the array is released, it will call release on all the objects in the array. You need n objects whether you init or copy - you still have multiple. Release them and then let the array release when it's released.
You need to create a unique instance for each object that is added. Also the obj needs to be released in this method because adding it to the array retains it. However if you are using ARC no releasing is needed/allowed.
self.myArray = [[NSMutableArray alloc] init];
MyObjClass *obj;
obj = [[[MyObjClass alloc] init] autorelease];
obj.firstName = #"First Name";
obj.lastName = #"Last Name";
obj.created = #"Dec 17 16:24";
[self.myArray addObject:obj];
obj = [[[MyObjClass alloc] init] autorelease];
obj.firstName = #"First Name2";
obj.lastName = #"Last Name2";
obj.created = #"Dec 18 7:41";
[self.myArray addObject:obj];
You are misunderstanding the -addObject: method and objects in general. You are adding a reference (pointer) to your instance of MyObjClass to the array. If you add the same instance twice, it doesn't make a difference. Also, as you only store a pointer to the object in the array, you always have the latest "version" of your object in the array.
You need to allocate two instances and add them both:
MyObjClass *object1 = ...;
[array addObject:object1];
MyObjClass *object2 = ...;
[array addObject:object2];
When adding objects to NSMutableArray, they are retained, not copied. If you don't want to modify your included object, use this sequence of code:
self.myArray = [[NSMutableArray alloc] init];
MyObjClass *obj = [[MyObjClass alloc] init];
obj.firstName = #"First Name";
obj.lastName = #"Last Name";
obj.created = #"Dec 17 16:24";
[self.myArray addObject:[[obj copy] autorelease]];
obj.firstName = #"First Name2";
obj.lastName = #"Last Name2";
obj.created = #"Dec 18 7:41";
[self.myArray addObject:obj]; // if you're using it further then you need to make a copy of it again
Try set the properties in your MyObjClass class like this:
#property (copy) NSString *firstName;
#property (copy) NSString *lastName;
#property (copy) NSString *created;

Append a NSString as the first line of another NSString

I have two NSString, A and B.
I would that A becomes B\nA.
How can I do?
If in a method I use
NSString *string_B = [[NSString alloc] initWithString:#"something_from_a_DB"];
NSString *string_A = [[NSString alloc] initWithString:#"something_from_a_DB"];
if (aTrueCondition) {
string_C = [NSString stringWithFormat:#"%#\n%#", string_B, string_A];
} else {
string_C = string_A;
}
is string_C = string_A; a memory leak or is it good?
I added [string_A release], as string_C is a retained property. Now it works.
This is the way to put them together:
NSString *newString = [NSString stringWithFormat:#"%#\n%#", stringB, stringA];
The second part is “A becoming newString”. This is hard to do, as regular strings are immutable in Cocoa. The best thing you can do is throw out the old A and point A to the new string:
NSString *strA = #"foo";
NSString *strB = #"bar";
strA = [NSString stringWith…];
Just be careful not to leak A:
NSString *strA = [[NSString alloc] initWithString:#"foo"];
strA = [NSString stringWith…]; // this is a leak
NSString *str=[NSString stringWithFormat:#"%#\n%#",B,A];
use this.
NSString *stringA = [NSString stringWithFormat:#"%#\n%#", stringB, stringA];

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

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]];