objective-C: simple question about copy/retain NSString - objective-c

If I set a NSString as property
#property (copy) NSString* name;
I always want to use (copy), so that if its value change, all object with such string still have the old value (as specifiedhere).
However, what happens if my NSString is not a class property, but it is just declared in the code ? Is in that case retained or copied everytime I assign a new value ?
thanks

It depends on how you declare it. You should read the documentation on memory management. Basically the rules are:
NSString *aString = [NSString stringWithString:#"Hello"];
NSString *bString = [NSString stringWithFormat:#"%#", #"Hello"];
In these cases, the string is not copied or retained. It is autoreleased, which means it will be automatically deallocated the next time the autorelease pool drains. You do not have to call the release method on them. (So assigning a new value will not cause it to leak.)
NSString *cString = [[NSString alloc] initWithFormat:#"%#", #"Hello"];
[cString release];
According to Objective C convention, methods that use alloc and have a retain count of one are not autoreleased, so you need to release them explicitly. Assigning a new value without releasing the old one will cause a leak.
You can also explicitly call a "copy" method or a "retain" method on a string. In either case, the new string will have a retain count of 1 and will not be autoreleased, so you will need to call the release method on it before you assign a new value.
NSString *dString = [cString retain];
NSString *eString = [cString copy];
...
[dString release];
[eString release];
If it is a property, and you use self.variableName, this will be taken care of for you (through the getters and setters which are generated with #synthesize). If you do it explicitly, you must make sure to call release on variables that you have called retain or copy on.
Edit: As some commentators below have noted, thinking about management in terms of "ownership" is usually the preferred of describing these ideas, rather than retain count.

If it's not a property and just declared in code, you need to explicitly retain or copy it, ie
NSString myString = [otherString copy];
or
NSString myString = [otherString retain];
Either way you also need to ensure it's released at somepoint.

If you're not using the property's setter, like self.name = #"foo" or [self setName:#"foo"], but rather assign the variable directly, like name = #"foo", it doesn't matter at all how the property is declared.
You have to understand that the property syntax is just a shortcut for writing accessor methods (-name and -setName: in this case). If you're not calling these methods (implicitly by setting the property), it doesn't matter how they work internally (which is what you specify by retain or copy).

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

__weak reference is still a mystery for me

NSString *myString = [NSString stringWithFormat:#"string1"];
__weak NSString *myString1 = myString;
myString= nil;
NSLog(#"%#, %#",myString,myString1);
I was expecting null , null. But the output is string1, (null). Why is myString1 still holding the value as myString is set to nil?
Weak references only get zeroed when the object is deallocated. That object is not immediately deallocated (it's probably in an autorelease pool here, though there are many other reasons something might be held onto in different situations), so the reference stays alive.
Try something like this:
NSString *myString;
NSString* __weak myString1;
#autoreleasepool{
myString= [NSString stringWithFormat:#"string1"];
myString1= myString;
myString= nil;
}
NSLog(#"%#, %#",myString,myString1);
Explanation
You probably noticed that there are many methods to allocate a string or generally an object:
1) [NSString stringWithFormat: ...] / [[NSString alloc]initWithFormat: ...] ;
2) [NSArray arrayWithArray: ...] / [[NSArray alloc]initWithArray: ...];
...
(Also for many other classes)
The first category of methods return an autoreleased object. The second one a non autoreleased object. Indeed if in the above code you use alloc + initWithFormat: instead of stringWithFormat: you don't need an autorelease pool to see that both objects will be nil.
I think your question may be answered by this quote from the Memory Management Guide
In particular, you should not design classes so that dealloc will be
invoked when you think it will be invoked. Invocation of dealloc might
be delayed or sidestepped, either because of a bug or because of
application tear-down.
The output should be (null), string1, not string1, (null). I guess you typed it the wrong way around.
You're explicitly setting one reference to nil, but the other reference is still being used within the scope of the definition (because you're using it in the NSLog). So, it won't be released by ARC until that usage is complete.
The weak reference isn't holding onto it. The fact that you're using it means that ARC will hold onto it (by not adding the release code). Once that usage is complete, ARC will release the object and then the weak reference will be nilled.

Basic of Objective C

#implementation GroupedInexedViewController
{
NSDictionary *names;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"PropertyList"
ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
names = dict;
[dict release];
}
Is deallocating 'dict' affects 'names'? I mean does it deallocate 'names' too? I saw in another post that its a bad practice? But why?
Edit: ARC is disabled.
In short, if you are not using ARC, yes: deallocating dict will affect names. This is because you are assigning the names pointer to the single NSDictionary you have allocated.
If you wanted to have names retain the NSDictionary when you dealloc dict, you would need to send dict a retain message:
names = [dict retain];
Since you're manually calling release, I'm going to assume you're not using Automatic Reference Counting (ARC).
There is some terminology mix up here.
It doesn't deallocate names. You're decrementing the reference count of dict when you call release. Once that reference count hits 0, the memory will be deallocated.
The problem is you assigned dict to names without first calling retain on dict.
Retaining an object increases it's reference count.
You can either choose to make *names a property, which will handle the memory management for you, or you can manually increment the reference count by calling retain: names = [dict retain];
If you do this, you must also implement a dealloc method and release names inside the dealloc method.
Your code assigns names with the value of dict. dict is a pointer to an object, so when you assign its value to another pointer (names), both pointers are referencing the same object and can be considered identical.
So yes, when you release dict, you are also releasing names.
BTW, you can assign to names directly without going through dict:
names = [[NSDictionary alloc] initWithContentsOfFile:path];
And if you can enable ARC, you never need to worry about releasing objects.
No answer so far seems to mention the difference between ARC and non-ARC (MRC) usage and the difference between properties and instance variables.
First of all, properties are really just setter and getter methods backed by an instance variable. When you set a property like self.dict = someObject; and the property was declared as strong or retain, then someObject is retained.
However, if you only have an instance variable (not a property) and you're using MRC, then merely writing dict = someObject; duplicates the pointer only but does not increase the reference count - if you write [someObject release] after this, you should assume that dict is invalidated as well (even if the object pointed to by these two pointers is not actually deallocated - this is a rule of reference counting).
If you are using ARC, then assigning to a variable increases the reference count by one as well - so if someObject has a reference count of 1, then writing dict = someObject; will increase the reference count of the object (now pointed to both by dict and someObject) to 2.
dict and names are two different variables, do not mix them!!!
"release" only when you alloc, copy, mutablecopy, retain.
Otherwise if gets created in autorelease mode.
And if you are using ARC, then no need of "release" compiler will take care of all these newly allocated spaces.

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.

+[NSString stringWithString:] -- what's the point?

As NSString strings are immutable, what is the value of the stringWithString: class method?
I get the utility when used with NSMutableString, I just didn't see the utility with the NSString class.
You might have a NSMutableString (or some home-grown NSString subclass) that you want to duplicate.
NSMutableString *buffer = [NSMutableString string];
// do something with buffer
NSString *immutableStringToKeepAround = [NSString stringWithString:buffer];
Of course, you can also just make a copy:
NSMutableString *buffer = [NSMutableString string];
// do something with buffer
NSString *immutableStringToKeepAround = [[buffer copy] autorelease];
but you own the copy and must release or autorelease it.
As "Andy" points out in #318666, it's related to memory management, quoting:
The difference between initWithString and stringWithString is that stringWithString returns an auto-released pointer. This means that you don't need to release it specifically, since that will be taken care of next time that the auto-release pool cleans up any auto-released pointers.
initWithString, on the other hand, returns a pointer with a retain count of 1 - you do need to call release on that pointer, or else it would result in a memory leak.
(Taken from here)
Returns a string created by copying the characters from another given string
[NSString stringWithString:#"some string"]
It is equivalent to
[[[NSString alloc] initWithString:#"some string"] autorelease]
Also, if you have a pointer to an NSString, it may actually be a subclass of NSString like NSMutableString. So, if you want to store the string and be guaranteed that it doesn't change, you should make a copy of it, hence stringWithString exists.
As another use case, if (for whatever reason) you create your own subclass of NSString or NSMutableString, stringWithString: provides a handy way to instantiate it with an instance of either NSString, NSMutableString, or MyCustomString.
I often use +stringWithString: when I need to create an NSMutableString but start it with an initial value. For example:
NSMutableString * output = [NSMutableString stringWithString:#"<ul>"];
for (NSString * item in anArray) {
[output appendFormat:#"<li>%#</li>", item];
}
[output appendString:#"</ul>"];
FYI, now that we are compiling with ARC enabled, you don't have to manually release at all, ARC will insert the release calls during compile time. So how is it still different? stringWithString is still added to the autorelease pool which gets drained sometime in the future (unless you created your own autorelease pool). initWithString will have a release call right before the function ends, so if you didn't retain it somewhere in the method, you can be sure that the string is destroyed by the end of the function call. This gives you a tighter control on the memory management as opposed to using autoreleased objects.