Retained NSString in AppDelegate lost after a PushScene - iphone

In my AppDelegate, near the end of the init method, I call a [self setup] method. This method gets a string from a URL, trims it, and assigns the string to a property called _songDirectory.
Here's how it looks in the header file:
#property (retain) NSString *_songDirectory;
And here is how it is assigned in the [setup] method:
//set a URL string
NSString *urlString = [NSString stringWithFormat:#"http://www.blahblahblah.com/php/dev/blah.php?routine=blah"];
NSMutableString *infoString = [self getInfoStringFromURL: urlString];
//get the song directory as a string from the infostring
NSRange startIndex = [infoString rangeOfString:#"song_directory=="];
NSRange endIndex = [infoString rangeOfString:#"end_of_data=="];
_songDirectory = [NSString stringWithString: [infoString substringWithRange: NSMakeRange(startIndex.location + startIndex.length, endIndex.location - startIndex.location - startIndex.length)]];
NSLog(#"STRING IN APP DELEGATE: %#", _songDirectory);
NSLog prints the correct string when called in the app delegate. However, after I push a new scene I cannot access _songDirectory from it. The following code in the pushed scene yields EXC_BAD_ACCESS:
NSLog(#"STRING IN PUSHED SCENE: %#", [[[UIApplication sharedApplication] delegate] _songDirectory]);
I can use the above statement to get ints from the app delegate but not strings. I would appreciate some insight!

You're assigning the string directly to the instance variable and not to the property, therefore the string is not retained. It should be self._songDirectory = ... instead of _songDirectory (and you should probably call the property songDirectory, the leading underscore is usually only used for private instance variables).

You need to use:
self._songDirectory = [NSString stringWithString: [infoString substringWithRange: NSMakeRange(startIndex.location + startIndex.length, endIndex.location - startIndex.location - startIndex.length)]];
in order to get the property to work. Otherwise you are just setting the ivar directly and the retain doesn't happen.

Related

How to assign another object to point single object? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
NSString immutable allows to change its values?
I want to point to an object where in, if I make changes to one object. the same change has to reflect to other object. How to do that?
I have done a sample, but it is not working.
Please check the code once. What is the mistake in my code?
-(void)viewDidLoad{
str = [[NSString alloc] initWithString:#"Taruni"];
str2=str;
str2=#"Kalpana chawla";
[self changeStr:str];
NSLog(#"str = %#",str);
[str release];
[str release];
[super viewDidLoad];
}
-(void)changeStr:(NSString *)x
{
x=#"Chandra";
}
In .h file, I have declared in this way
#property(nonatomic,assign) NSString *str;
#property(nonatomic,assign) NSString *str2;
If I change the value of str2, str is not getting reflected.
How to do that?
Try below code,
-(void)viewDidLoad{
NSString *str = [[NSString alloc] initWithString:#"Taruni"];
NSString **str2 = &str;
*str2=#"Kalpana chawla";
[self changeStr:&str];
NSLog(#"str = %#",str);
[str release];
[str release];
[super viewDidLoad];
}
-(void)changeStr:(NSString **)x
{
*x=#"Chandra";
}
First of all, if you want to change the content of a string you need NSMutableString.
Second of all, in your changeStr: method you did nothing but assigned the "x" pointer to point to another string (and x is local).. In method your pointer "x" is local and assignment (x = something) will not reflect outside of that method. If you want to change something outside you will have to use x.propertyName or [x methodName]. If you want to change your input as to what string does it point to, you need input as :(NSString **)x, then *x = #"Chandra" and [self changeStr:&str].
Third of all, you can not change constant strings (defined like this: #"constant string"), you will have to use [NSString stringWithFormat:#"%#", str] (rather yet NSMutableString for your purpose)

NSMutableString stringWithString giving crash

ten.textValue = [[NSMutableString alloc]init];
ten.textValue = [NSMutableString stringWithString:textField.text];
I am getting crash at second line.
ten.textValue is NSMutableString.
It is probably because the text property of UITextField is nil by default, and passing nil to [NSMutableString stringWithString:nil] causes a crash.
You need to make sure the text is not nil when you pass it to be copied, for example like this:
[NSMutableString stringWithString: textField.text ? textField.text : #""]
You should also eliminate the first line - it serves no purpose, because the allocated and assigned value gets overwritten immediately.
When you create your ten.textValue = [[NSMutableString alloc]init]; you are creating an object that you own.
When you try to add a string to it in the next line, you are creating an autoreleased string. This is confusing the compiler, which is reporting "hang on - this is an allocated, owned object already".
Instead:
if(ten.textValue)
{
ten.textValue = [NSMutableString stringWithString: textField.text]};
}

Why does this cause a crash?

I have these two buttons hooked up to these two methods (they're nearly identical)
-(void)moveOneImageNewer{
int num = [numberOfImage intValue];
num--;
numberOfImage = [[NSString stringWithFormat:#"%i",num] retain];
//Load the image
[self loadImage];
}
-(void)moveOneImageOlder{
int num = [numberOfImage intValue];
num++;
numberOfImage = [NSString stringWithFormat:#"%i",num];
//Load the image
[self loadImage];
}
If I hit either of them twice (or once each, basically if they get called a total of two times) I get an EXC_BAD_ACCESS. If I throw a retain on: numberOfImage = [[NSString stringWithFormat:#"%i",num]retain] it's fine though. Can someone explain why this is? I did an NSZombie on the instruments and traced it back to this stringWithFormat call. Thanks in advance!
+stringWithFormat: doesn't contain 'new', 'alloc', 'copy', or 'retain', so it should be expected that you have to retain the return value of it if you want the new NSString it creates to stick around.
Edited to include this handy link duskwuff kindly dug up: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-BAJHFBGH
If numberOfImage is a properly declared property, e.g.
#property (copy) NSString *numberOfImage;
and it was properly synthesized (in the #implementation section for the class):
#synthesize numberOfImage;
then you can do:
- (void) moveOneImageNewer
{
self.numberOfImage = [NSString stringWithFormat: #"%i", [self.numberOfImage intValue] - 1];
// Load the image
[self loadImage];
}
The property setter will take care of retaining the string and, if necessary, releasing the previous string.
FWIW, why on earth is numberOfImage a string? Why not a simple int?
numberOfImage is an instance variable or property of your class, right?
You are setting it to a stringWithFormat (which returns an auto-released NSString) without claiming ownership of that object (by calling retain).
If you do not retain it, it will get auto-released before the method is called again (and then the first line will fail, as it tries to access the previously set, now auto-released value).
Consider using properties, they have auto-generated memory management code (including releasing the old NSString when you set the new one).
You haven't retained the string object in "moveOneImageOlder", so that object gets autoreleased at the end of the event cycle and points to nothing. That's why you get the EXC_BAD_ACCESS next time you try to use it.
Use a retain to claim ownership of the NSString. Remember to release when you're done though (you can use properties to help you with this)
-(void)moveOneImageNewer{
int num = [numberOfImage intValue];
num--;
[numberOfImage release];
numberOfImage = [[NSString stringWithFormat:#"%i",num] retain];
//Load the image
[self loadImage];
}
-(void)moveOneImageOlder{
int num = [numberOfImage intValue];
num++;
[numberOfImage release];
numberOfImage = [[NSString stringWithFormat:#"%i",num] retain];
//Load the image
[self loadImage];
}
Add this in dealloc:
- (void)dealloc {
[numberOfImage release];
[super dealloc];
}
Well, the NSString class method "stringWithFormat" returns an autorelease NSString object if I'm right.
So the second call to your method would have numberOfImage pointing to nothing, as the autorelease NSString object it used to be pointing to has already been released and deallocated since you didn't retain it.
The part that is directly causing the crash is [numberOfImage intValue] when you call the method a second time, as you sending a message to an object (pointed to by numberOfImage) that no longer exist.

Should I release #property (nonatomic, copy)?

I'm using UISearchBar, and one of its properties, text, is declared as follows:
Abstract: The current or starting search text.
Declaration: #property(nonatomic, copy) NSString *text;
I know that the rule is to release what ever you use +alloc, +new or -copy.
I did:
NSString *searchText = searchBar.text;
And:
[searchText release];
And I got a nice EXC_BAD_ACCESS message. When I removed the release line, the EXC_BAD_ACCESS message stopped to appear, so I assumed that it is the eror source.
The question: Shouldn't I release searchText, since it comes from a property that uses copy?
No, you should not use release here. The "copy" in this case refers to how the setter is implemented, not the getter. The call you made (-text) does not include the word "copy" so you should not release the result.
The copy attribute of the property means that the object is copied before assigning to the instance variable. When you access this property you then get a reference to the copy that was made.
When you set the the text on the searchbar:
NSString* myTextString = [[NSString alloc] initWithString:#"My Text String"];
mySearchBar.text = myTextString;
[myTextString release];
To elaborate on Rob Napier's correct answer:
NSString *searchText = searchBar.text;
This code assigns a reference to the text property of searchBar to searchText. This is not a copy of the searchText, just another reference to the same NSString object in the searchBar object. Releasing searchText is the same as releasing searchBar.text, which cause your EXC_BAD_ACCESS message.
In this declaration of the text property, the getter method is merely:
- (NSString *)text {
return text;
}
The more interesting method is the setter method. For this declaration, the setter is similar to:
- (Void)setText:(NSString *)newString {
if (text != newString) {
[text release];
text = [newString copy];
}
}

How to transfer values between classes on iPhone?

I want to send a string from one class to the other:
1) In class1, I add a property to hold the string:
#property (nonatomic, retain) NSString *str;
2) and a method to send back a string:
-(NSString *)sendBackStr:(NSString *)strURL
{
NSString *str = [[NSString alloc] stringWithString:strURL];
return str;
}
3) In class2, I add a property to hold the received string:
#property (nonatomic, retain) NSString *returnStr;
4) and the following code:
Class1 *c1 = [[Class1 alloc] init];
returnStr = [c1 sendBackStr:#"URL"];
But the program stops at returnStr = [c1 sendBackStr:#"URL"]; Any ideas about what's wrong with it?
stringWithString is a class method returning an autoreleased string. You should be calling it like this:
myProperty = [NSString stringWithString:strURL];
Here I assume your property does a copy, to increment the retain count on the autoreleased string that's returned from the stringWithString method. (Objects returned from alloc calls have a retain count of one and are not autoreleased.) It's more usual to give strings the copy property rather than the retain one - you usually just want your own copy of a string, not a shared reference to a string owned by someone else.
What I also can't understand is why you've written the code like this, unless it's just an example. In class 2, all you need to do is write
returnStr = [NSString stringWithString:#"URL"];
stringWithString: is a message that needs to be sent to the NSString class, not an instance of your class (returned via alloc).
The correct code should be:
-(NSString *)sendBackStr:(NSString *)strURL
{
return [NSString stringWithString:strURL];
}
You might want to familarize yourself more about the idioms around allocation, retention, and autoreleasing of pointers. If you wanted to alloc this string for some reason and return it from the sendBackStr: message, then you would probably want this code:
-(NSString *)sendBackStr:(NSString *)strURL
{
return [[[NSString alloc] initWithString:strURL] autorelease];
}