Objective-C NSString Assignment Problem - objective-c

In my Cocoa application, in the header file, I declare a NSString ivar:
NSString *gSdkPath;
Then, in awakeFromNib, I assign it to a value:
gSdkPath = #"hello";
Later, it's value is changed in the code:
gSdkPath = [NSString stringWithString:[folderNames objectAtIndex:0]];
(the object returned from objectAtIndex is an NSString)
However, after this point, in another method when I try to NSLog() (or do anything with) the gSdkPath variable, the app crashes. I'm sure this has something to do with memory management, but I'm beginning with Cocoa and not sure exactly how this all works.
Thanks for any help in advance.
EDIT: This was solved by retaining the string [gSdkPath retain].

(the object returned from
objectAtIndex is an NSString)
Are you sure? I suggest putting this in it's own temporary variable and double checking that it's not nil or invalid in some way.
Edit: If that is OK so far, do note that stringWithString returns an autoreleased object. You need to retain it if you want to use it "later".
gSdkPath = [NSString stringWithString:[folderNames objectAtIndex:0]];
[gSdkPath retain];

Related

Difference between Strong and Weak references (Using ARC) Please No Theory..I know the difference theoretically

I have been trying to understand the difference between Strong and Weak references in iOS. What I did to understand is:
//.h File
#property(nonatomic,strong) NSString* myStrongString;
#property(nonatomic,weak) NSString* myWeakString;
//.m File
- (void)viewDidLoad
{
[super viewDidLoad];
[self assignTempString];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)assignTempString{
self.myStrongString = [[NSString alloc] initWithString:#"Varun Mehta"];
}
- (IBAction)printAssignedString:(id)sender {
NSLog(#"Object will have strong reference so it will print my name==%#",self.myStrongString);
}
According to my understanding when I repeat the above step by using myWeakString it should print null. But its still printing my name. Anybody having any idea why its happening.
But when I replace [[NSString alloc] initWithString:#"Varun Mehta"] with [NSString stringWithFormat:#"Varun Mehta"] or [[NSString alloc] initWithFormat:#"Varun Mehta"] result is coming as I have expected.
There are several things to consider here.
A statically declared string is built into your app so it isn't really retained or released, thus a weak reference to #"my string" will always be valid. The compiler is just recognizing [[NSString alloc] initWithString:#"Varun Mehta"] as a static string and removing your alloc/init. However anything that deals with formatting is, by definition, creating a new string and thus the new string obeys the weak referencing rules and is immediately deallocated, nil-ing out the reference.
If you access a weakly retained object that ends up in the autorelease pool it won't actually get deallocated until all your methods return and the run loop goes back into another cycle (and thus drains the autorelease pool), so you can continue to work with the object even though it is "walking dead". This is typically only when interacting with non-ARC code.
If you need practise try this code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self assignTempString];
}
-(void)assignTempString{
#autoreleasepool
{
self.myStrongString = [NSString stringWithFormat:#"%#", #"Strong string"];
self.myWeakString = [NSString stringWithFormat:#"%#", #"Weak string"];
}
}
- (IBAction)printAssignedString:(id)sender {
NSLog(#"Strong ptr content: %#",self.myStrongString);
NSLog(#"Weak ptr content: %#",self.myWeakString);
}
[NSString alloc] will allocate an ARC-managed object and will set its retain count to 1. As long as your view controller is alive, this retain count will be 1, so it will not be deallocated. [NSString stringWithFormat:] returns an autoreleased string which is deallocated after the execution of [self assignTempString].
Two methods initWithString and stringWithFormat suggest exactly what is to expect.
So initWithString expects you to create allocate memory and then initialise it.
While stringWithFormat expects you to just point to the string.
When you do a init with your strong/weak variable it will exist till end of your program.
While when you point;
strong literal will keep a reference and hence will not allow ARC to cleanup the string literal,
weak literal will not keep a reference and hence ARC is free to clean it up immediately after the function call.
Hope it clarifies working for you.
What you are experiencing happens because of how NSString is implemented.
Since NSString objects are immutable the compiler takes a shortcut when you use stringWithString: with a string literal as argument. If the argument of this and other related methods is a string literal the returned value will just point to the string literal. The whole object instantiation is optimized away.
And string literals won't be deallocated. But the weak variable is only nil'd out during dealloc, so if dealloc is never called the weak variables are never set to nil.
This won't happen if you use stringWithFormat:. Even using only string literals as argument will create new string instances.
Why? Most likely because Apple decided that it's not worth the effort to check if stringWithFormat: was used with a string literal that does not have any format specifiers.
That's an implementation detail, don't think too long about this decision. It should not influence the code you write. I would suggest you treat every string that is not a bare literal (i.e. #"Foo" without any NSString methods) as dynamically created NSString (i.e. use isEqualToString: for all your string comparisons)
This logging code will show this reuse behaviour. It'll show the same addresses for all NSString instances, because the compiler has optimized all those calls to a simple #"Foo".
NSLog(#"%p", #"Foo");
NSLog(#"%p", [[NSString alloc] initWithString:#"Foo"]);
NSLog(#"%p", [NSString stringWithString:#"Foo"]);
NSLog(#"%p", [[NSString stringWithString:#"Foo"] copy]);
NSLog(#"%p", [#"Foo" copy]);
In newer versions of Xcode you will even get nice warnings for this code:
using initWithString: with a literal is redundant
using stringWithString: with a literal is redundant

Memory Management Questions

I have been programming in Objective-C for a couple weeks now, and I think its time that I think more about my memory management and so I have a couple scenarios I would like clarification on.
NSString *myString = #"Hello, World";
If I were to on the next line do
NSString *anotherString = myString;
Is it getting copied? or is another string just a pointer to myString? if it's just a pointer how would I make anotherString a copy of myString that has its own memory?
What if I were to
[anotherString release];
What actually gets released, just anotherString, or does myString go away too?
Then if I were to do
anotherString = nil;
Does that actually free up memory and for use or does it still have the space allocated with empty memory? Obviously the pointer can still be used. What does it do to myString?
This will really help me become better at managing the memory properly, instead of just [object release]; randomly and causing crashes.
Thank you VERY MUCH in advance to whoever replies.
When you do:
NSString *myString = #"Hello, World";
NSString *anotherString = myString;
...you're creating a single NSString instance, and two pointers to it. So to answer your questions, no it is not being copied. Yes, anotherString is just a pointer, and so is myString, the actual NSString instance is off in memory-land, at the address that both pointers are now pointing to.
To make a copy of the string, you would do:
NSString *anotherString = [myString copy];
When you do:
[anotherString release];
The NSString instance that anotherString points to is released. Nothing happens to anotherString itself. It continues to point to your now-invalidated object instance. If myString was pointing to the same instance, then yes, this causes myString to "go away" as well.
And if you do:
anotherString = nil;
...then you've caused a memory leak, because you reassigned the pointer without releasing the thing it was pointing at. Except not in this case because #"Hello, World" is a literal value that is loaded onto the stack and is not retained by your code.
In any case, setting anotherString to nil does nothing to your string, it just changes what anotherString points to. Your actual NSString instance is still off in memory-land, completely unaware that you just threw away your reference to it (possible exception: reference counted/garbage collected environments).
And yes, calling [object release] randomly will cause crashes. As a general rule you should only call release on something if:
You called alloc and init... on it.
You called retain on it.
You obtained the object as a result of calling copy on some other object.
Note that these stack, so if you alloc something, retain it, and then copy it, you need to have 3 calls to release, two for the object that you alloc'ed, and one for the copy.
As a side note, you may find it clearer to write your object declarations like:
NSString* myString = #"Hello, World";
NSString* anotherString = myString;
...which makes it more obvious that what you are declaring are pointers to NSString's and not NSString's themselves. Personally I think your original syntax, while commonly used, is also backwards.
This string:
NSString *myString = #"Hello, World";
is created on the stack. It's loaded into memory when the app starts. You don't own the memory backing the object, and therefore shouldn't release it.
If you want to copy a string to the heap (where you can manage the memory), you simply do this:
NSString *anotherString = [myString copy];
Then you're responsible for releasing that object when you're done with it.
[anotherString release];
Setting a pointer to nil just clears the pointer, not the object it's pointing to. It's still in memory.
NSString *anotherString = myString;
--- This is just a pointer assignment
--- You can copy myString using [myString copy]
[anotherString release];
--- You are releasing the owner ship of the anotherString (please read about retain count concept)
anotherString = nil;
--- you are just assigning the pointer to nothing.
I suggest Please read more about memory management apple documents ...

When not to alloc and init an NSString

Whenever I need to create a new NSString variable I always alloc and init it. It seems that there are times when you don't want to do this. How do you know when to alloc and init an NSString and when not to?
Whenever I need to create a new NSString variable I always alloc and init it.
No, that doesn't make sense.
The variable exists from the moment the program encounters the point where you declare it:
NSString *myString;
This variable is not an NSString. It is storage for a pointer to an NSString. That's what the * indicates: That this variable holds a pointer.
The NSString object exists only from the moment you create one:
[[NSString alloc] init];
and the pointer to that object is only in the variable from the moment you assign it there:
myString = [[NSString alloc] init];
//Or, initializing the variable in its declaration:
NSString *myString = [[NSString alloc] init];
Thus, if you're going to get a string object from somewhere else (e.g., substringWithRange:), you can skip creating a new, empty one, because you're just going to replace the pointer to the empty string with the pointer to the other one.
Sometimes you do want to create an empty string; for example, if you're going to obtain a bunch of strings one at a time (e.g., from an NSScanner) and want to concatenate some or all of them into one big string, you can create an empty mutable string (using alloc and init) and send it appendString: messages to do the concatenations.
You also need to release any object you create by alloc. This is one of the rules in the Memory Management Programming Guide.
If you want to initialise it to a known value, there is little point in using alloc, you can just use a string literal:
NSString* myStr = #"Some value";
If you want to initialise it with a format or whatever, but don't need it to stick around beyond the current autorelease pool lifetime, it's a bit neater to use the class convenience methods:
NSString* myTempStr = [NSString stringWithFormat:#"%d", myIntVar];
If you need its lifetime to go beyond that, either alloc/init or else add a retain to the previous call. I tend to slightly prefer the latter, but the two are pretty much equivalent. Either way you will need a balancing release later.
Note that, since NSString is not mutable, this sort of thing is not only unnecessary but actively wrong:
// don't do this!
NSString* myStr = [[NSString alloc] initWithString:#""];
myStr = someOtherStr;
since it leaks the initial placeholder value.
It seems that there are times when you don't want to do this.
I can't think of any time when I would want to alloc/init a NSString. Since NSStringgs are immutable, you pretty much always create new strings by one of:
convenience class method e.g.
NSString* foo = [NSString stringWithFormat:...];
literal
NSString* foo = #"literal";
NSString instance method
NSString* foo = [bar uppercaseString];
copy from mutable string
NSString* foo = [mutableBar copy]; // foo needs to be released or autoreleased in this case
I'm guessing that you are referring to using StringWithString or similar instead of initWithString? StringWithString alloc and inits for you under the hood and then returns an autoreleased string.
If you don't need to do any string manipulation other than to have the string, you can use NSString *str = #"string";
In general with iOS, the tighter you manage your memory the better. This means that if you don't need to return a string from a method, you should alloc init and then release it.
If you need to return a string, of course you'll need to return an autoreleased string. I don't think its any more complicated than that.

confused about NSString *str

I am kind of confused by the behavior of NSString *str..
I assigned it in several ways, sometimes it works, and sometimes it becomes null.
NSString *str = #"/hi/hello"; // this one always works
// this sometimes becomes null after the function ends
NSString *str2 = [str lastPathComponent];
// as above
NSString *str3 = [NSString stringWithString:str2];
NSString *str4 = [NSString initWithString:str3];
I am not quite familiar with the object behavior of Obj-C, is it just like C++?
If so, how can I do assignment safely like
string str = "hi";
string str2 = str;
behaves in C++?
ex: I declare a string in my .h file,
how to assign it safely that it wouldn't become NULL after a function ends?
I know it's a very basic question, but I can't find the answer in NSString reference page.
Really thanks for any help!
The behaviour is not just like C++. Objects are reference-counted. If you want to keep one around, you must place a claim on it.
If you create the object yourself with a method whose name includes the word alloc, new or copy then you have ownership already. This is like a C++ new. (When you have created an object with alloc, you need also to initialise it with some method whose name begins init. But you have to create it first. In C++ both things would be considered parts of the single act of construction.)
Objects you receive from other methods (such as two of the three NSString methods you mention) are only transiently available unless you explicitly claim ownership by calling [object retain]. You only need to do this if you want to keep them around beyond the immediate context. (There isn't really an equivalent to this in C++.)
However you gain ownership, you must relinquish it when you are finished by calling [object release]. This sort of like a C++ delete, except that the object isn't actually destroyed until all ownership claims are released.
Getting to grips with this is really really really important, perhaps the only important thing you need to know to use Objective-C. Read the object ownership documentation carefully and you'll be sorted.
I assume you're not using garbage collection? If this is the case then you need to retain the string.
NSString* str2 = [[str lastPathComponent] retain];
I suggest you do some reading on objective-c memory management.
NSString *str = #"/hi/hello";
This works because it creates a string literal. Answers to this question are worth a read to understand these in Objective-C
What's the difference between a string constant and a string literal?
In all these cases you are creating autoreleased strings. These will be deallocated when you return to the application's runloop.
NSString *str2 = [str lastPathComponent];
NSString *str3 = [NSString stringWithString:str2];
In this last one I assume you meant [[NSString alloc] initWithString:str3]
This creates a string that is retained. But this isn't a good way to create static strings.
You should create static strings in your implementation file like this
static NSString *myConstant = #"constantString"

Do I need to release NSString generated using #"..."?

If I make an NSString using the code below, do I need to need to release someString?
NSString *someString = #"somestring";
No, it's a compile time constant string object, so it doesn't need releasing. It's the moral equiv of char *c = "hello world" -- where the string hello world is in global data, and you're assigning the address of this data to the pointer c.
If you created an object via a method call that contains alloc, retain, or copy, or starts with new (N-A-R-C = "narc"), then you are responsible for releasing the object. If this is not the case, then you can ignore the object.
So in the case of strings:
NSString * myString = #"This is a string";
I don't see a call there to a NARC method, so you are not responsible for releasing it. It's really that simple.
No, since it's a compile-time constant string, you do not need to release it. In fact, doing so will likely cause a run-time error.
I checked this case is different from NSString *someThing = #"someThing";
they should release urlString because of
[[NSString alloc] initWithFormat:#"%#%#", baseURLString, queryTerm];
Anywhere you use alloc/init you release it no mater what.
If it's a compile-time constant string, there wouldn't be a need to retain it as well. Is it correct?
So, please check what Apple does under the section "Insert Data Using a POST Request" at the following link:
I see a [urlString release];, why?