Alter NSString to get nothing back - objective-c

At two stages of my app's runtime, I am sending an NSString to the following method.
I receive no warnings or errors in the code editor or the debugger, but when I NSLog the output (secondString), I only get given the memory address of the object.
Is there something I am not doing right here?
- (NSString*)validateString
{
NSString *firstString = [NSString stringWithFormat:#"%#", self];
[firstString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableString *secondString = [firstString mutableCopy];
[secondString replaceOccurrencesOfString:#"&" withString:#"%26" options:NSCaseInsensitiveSearch range:NSMakeRange([secondString length], 0)];
secondString = [NSString stringWithFormat:#"%#", secondString];
NSLog (#"%# and %#", firstString, secondString);
return secondString;
[firstString release];
[secondString release];
}
I'd appreciate any help.
Thanks,
Ricky.

Ugh that code is wrong on so many levels.
Here's a simpler version:
- (NSString*)validateString {
NSString *firstString = [self stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *secondString = [firstString stringByReplacingOccurrencesOfString:#"&" withString:#"%%26" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [firstString length])];
NSLog (#"%# and %#", firstString, secondString);
return secondString;
}
The construct [NSString stringWithFormat:#"%#", aString] is a pretty useless statement, especially when you have copy available. You also have a memory leak and a crash in your code (you create a copy of a string [+1 retain count], assign an autoreleased string into the same variable [+0 retain count, original string lost and leaked], and then release the autoreleased string [crash when the autorelease pool drains]).

First some comments:
1) Everything after the return will not be executed, so the last to statements are useless (dead code).
2) If not created with +alloc, you can assumed that NSString instances are autoreleased, thus you do not need to send the -release message to firstString.
Edit: As Peter Hosey pointed out, you must however release the string obtained by -mutableCopy.
To answer your question:
-stringByAddingPercentEscapesUsingEncoding: returns a pointer to the newly created instance, so you have to save it.
- (NSString*)validateString
{
NSString *firstString = [NSString stringWithFormat:#"%#", self];
firstString = [firstString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableString *secondString = [firstString mutableCopy];
[secondString replaceOccurrencesOfString:#"&" withString:#"%26" options:NSCaseInsensitiveSearch range:NSMakeRange([secondString length], 0)];
secondString = [NSString stringWithFormat:#"%#", secondString];
NSLog (#"%# and %#", firstString, secondString);
[secondString release];
return secondString;
}

You need to use %%26 if you want the string "%26"
your NSMakeRange is backwards
your return is too early, and you don't need to release the strings anyway

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

NSString.length gives EXC_BAD_ACCESS

I have the following code:
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
NSString *temp;
for (int i = 0; i < [array count]; i++)
{
temp = [array objectAtIndex:i];
NSLog(#"temp length = %#", [temp length]);
}
I get an EXC_BAD_ACCESS error at the NSLog line. I assume it's erring out at the [temp length] bit. The weird thing is, I can do other methods of NSString on temp and they work fine, like [temp characterAtIndex:0].
I've also tried doing [[array objectAtIndex:i] retain];, but that doesn't seem to help.
Does anyone know why I'm getting this error?
EDIT: Turns out it was crashing at the NSLog because it was %# instead of %lu. The real problem was with other code that I had omitted from this post. After playing around with it some more, I got it working.
From my understanding, the "%#" placeholder is for object pointers, "length" returns "NSUInteger" which is not a pointer. Try "%lu" instead of "%#".
This (slightly cleaned up) version works for me:
NSError *error = nil;
NSString *path = [#"~/Desktop" stringByExpandingTildeInPath];
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:
path error:&error];
if (error) NSLog(#"%#", error);
for (NSString *path in array) {
NSLog(#"Path length = %lu", path.length);
}
As thg435 mentioned, "%#" is for object pointers, so if you pass it an arbitrary number it will throw a memory access error.

EXC_BAD_ACESS error

I get that error EXC_BAD_ACESS at the following line:
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionTitle",i];
Here is the for loop where the above code line is located:
for (i=0; i < count; ++i)
{
//Save the occasionS details to NSUserDefaults
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionTitle",i];
NSString *dateVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionDate",i];
NSString *imageVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionImage",i];
[[NSUserDefaults standardUserDefaults] setValue:[[[self displayedObjects] objectAtIndex:i]
title] forKey:titleVarName];
[[NSUserDefaults standardUserDefaults] setValue:[[[self displayedObjects] objectAtIndex:i]
date] forKey:dateVarName];
[[NSUserDefaults standardUserDefaults] setValue:[[[self displayedObjects] objectAtIndex:i]
imagePath] forKey:imageVarName];
//release
[titleVarName release];
[dateVarName release];
[imageVarName release];
[self dismissModalViewControllerAnimated:YES];
}
Isn't ok to alloc objects and release them inside a for loop?
You need to use %d or %i specifier instead of %# to specify an integer. If %# is used with int then it will try to access the object at the address specified by the int. For example, if the value of i is one then it is trying to access the object at address one which will cause a bad access.
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%d",#"occasionTitle",i];
And also you don't need alloc and release here, though that is not the reason of bad access. You can use a convenience constructor.
NSString *titleVarName = [NSString stringWithFormat:#"occasionTitle%d", i];
// release not required
Do the same for dateVarName and imageVarName too.
Assuming i is an int, that line should be
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%i",#"occasionTitle",i];
%# is used for Cocoa objects, not primitives like an int, float or bool;
Use the %# format specifier only for NSObject objects.
As i is an integer in your code, you have to use %d or %i for integers.
Moreover, there is no need to include the string using %#, you can use the static string directly in your format string:
NSString *titleVarName = [[NSString alloc] initWithFormat:#"occasionTitle%i",i];

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

How to remove NSString Related Memory Leaks?

in my application this method shows memory leak how do i remove leak?
-(void)getOneQuestion:(int)flashcardId categoryID:(int)categoryId
{
flashCardText = [[NSString alloc] init];
flashCardAnswer=[[NSString alloc] init];
//NSLog(#"%s %d %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__);
sqlite3 *MyDatabase;
sqlite3_stmt *CompiledStatement=nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *MyDatabasePath = [documentsDirectory stringByAppendingString:#"/flashCardDatabase.sqlite"];
if(sqlite3_open([MyDatabasePath UTF8String],&MyDatabase) == SQLITE_OK)
{
sqlite3_prepare_v2(MyDatabase, "select flashCardText,flashCardAnswer,flashCardTotalOption from flashcardquestionInfo where flashCardId=? and categoryId=?", -1, &CompiledStatement, NULL);
sqlite3_bind_int(CompiledStatement, 1, flashcardId);
sqlite3_bind_int(CompiledStatement, 2, categoryId);
while(sqlite3_step(CompiledStatement) == SQLITE_ROW)
{
self.flashCardText = [NSString stringWithUTF8String:(char *)sqlite3_column_text(CompiledStatement,0)];
self.flashCardAnswer= [NSString stringWithUTF8String:(char *)sqlite3_column_text(CompiledStatement,1)];
flashCardTotalOption=[[NSNumber numberWithInt:sqlite3_column_int(CompiledStatement,2)] intValue];
}
sqlite3_reset(CompiledStatement);
sqlite3_finalize(CompiledStatement);
sqlite3_close(MyDatabase);
}
}
this method also shows leaks.....what's wrong with this method?
-(void)getMultipleChoiceAnswer:(int)flashCardId
{
if(optionsList!=nil)
[optionsList removeAllObjects];
else
optionsList = [[NSMutableArray alloc] init];
sqlite3 *MyDatabase;
sqlite3_stmt *CompiledStatement=nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *MyDatabasePath = [documentsDirectory stringByAppendingString:#"/flashCardDatabase.sqlite"];
if(sqlite3_open([MyDatabasePath UTF8String],&MyDatabase) == SQLITE_OK)
{
sqlite3_prepare_v2(MyDatabase,"select OptionText from flashCardMultipleAnswer where flashCardId=?", -1, &CompiledStatement, NULL);
sqlite3_bind_int(CompiledStatement, 1, flashCardId);
while(sqlite3_step(CompiledStatement) == SQLITE_ROW)
{
[optionsList addObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(CompiledStatement,0)]];
}
sqlite3_reset(CompiledStatement);
sqlite3_finalize(CompiledStatement);
sqlite3_close(MyDatabase);
}
}
alt text http://www.freeimagehosting.net/uploads/5b8120982c.png
You don't actually use the objects you initialise at the top of the function:
flashCardText = [[NSString alloc] init];
flashCardAnswer=[[NSString alloc] init];
as you replace those objects with others later on:
self.flashCardText = [NSString stringWithUTF8String:(char *)sqlite3_column_text(CompiledStatement,0)];
self.flashCardAnswer= [NSString stringWithUTF8String:(char *)sqlite3_column_text(CompiledStatement,1)];
So those would seem to be the objects that are leaking.
[[NSString alloc] init] is completely pointless. It will return exactly equivalent of #"", except wiht more work. Its unlikely to leak, since the system will almost certainly return you a fixed constant empty string, releasing the [NSString alloc] in the init routine. But it is pointless and useless and bad code.
Other than that, your code looks OK. The second method might be considered to "leak" optionsList, simply because it creates it and it is never released, but its only created once so it should be fine.
Try running your program and doing the leak detection, then breaking in the debugger and using
po 0x4b2720 (replace with the address of the leaked objects)
to see what string is actually leaking.
Remember that Leaks can give false positives, especially if anything is cached.
[[NSString alloc] init]; is a useless phrase. Removing the two first lines would get rid of the leak.
EDIT: Note, too, that the two strings you pull from the database will vanish as soon as the autoreleasepool is drained, unless they are retained.
Redit: Concerning the second method; I cannot see any obvious leaks. NSCFStrings are created a lot, and often stick around. That doesn't mean they actually are leaks. From what I can see, everything in that method is either autoreleased or persistent.
-1/2
#"" is equivalent to [[[NSString alloc] init] autorelease];
[[NSString alloc] init] will leak.
Remember the rule: init/retain should be balanced by autorelease/release.