Difference bewteen declaring a NSString with alloc and init and assigning with #"myString" - objective-c

Currently I'm troubled with a small understanding issue on Objective-C. As example following Aaron Hillegass's book, I'm wondering about assigning an NSString in the init method of a class a value like in this example (For people who know the book, this is used in the Person class of RaiseMan):
- (id)init
{
if(![super init])
return nil;
myString = #"New entry";
return self;
}
This string isn't allocated by me, so normally I shouldn't bother about releasing it.
BUT! What happens in a setter-method of this string? Following the memory management rules the method should look like:
- (void)setMyString:(NSString *)newString
{
if(myString != newString) {
[myString release];
[newString retain];
myString = newString;
}
}
Why does [myString release] work? I've read somewhere, that with = #"bla" assigned strings can't be released.
And is initializing with = #"bla" the right way? Or should I use alloc and init instead?
Thanks for any help :)

NSString *constantString = #"constantString";
String like constantString are said to be from a private(?) class NSConstantString and they are alive through all your program life. Off-course release and retain work, (in the mean that they won't give you a exception or crash) They just do nothing.
Read more here
Also you said in one of your comments that it would be a{#property(..., copy) NSString myString;But what you are showing us is a typical #property(..., retain)

AFAIK, string constants of the form #"..." are actually a child class of NSString that redefine retain and release as no-ops. This allows the compiler to store those string constants in the data segment of your executable instead of on the heap.

Is myString declared in the header-file? Like: #property(nonatomic, retain) NSString myString. If that is the case, then myString is retained. Otherwise, it's not necessary to release it.

Related

Retain and release nsstring

str is a NSString I own. It is my responsibility to release it,
since I call initWithString:
if (![[str substringFromIndex:str.length-1] isEqualToString:#"\n"])
{
str = [str stringByAppendingString:#"\n"];
}
if the line inside the if statement reached, I will lose the ownership of the str var.
so my app crashes with a zombie instance, when I release str later:
[str release];
All goes fine if the if statement is NO(false).
What can I do to maintain the ownership of str?
Note that str could be very long I don't want to init another NSString
You need to do normal memory management:
if (![[str substringFromIndex:str.length-1] isEqualToString:#"\n"])
{
NSString *newStr = [str stringByAppendingString:#"\n"];
[str release];
str = [newStr retain];
}
Keep in mind that stringByAppendingString: returns an autoreleased string (and it also creates a whole new string).
Suggestion 1: Use ARC. It solves these problems for you. This is by far the best solution.
As rmaddy says, Xcode has an automated tool for converting apps to ARC. Look in the edit menu, under refactor>Convert to Objective-C ARC. The process is fairly painless. It flags things it wasn't able to figure out on it's own (usually only a few things.) After you clean up those issues you are off and running and never have to worry about retain counts again.
Suggestion 1a: Make str a mutable string, as #rmaddy suggested.
Then your code would look like this:
[str appendString: #"\n"];
That's simpler, easier to read, more memory-efficient, and works exactly the same in both ARC and manual reference counting.
Failing that, change str to be a retained property
#property (nonatomic, retain) NSString *str);
Then use property notation:
if (![[self.str substringFromIndex: self.str.length-1] isEqualToString:#"\n"])
{
self.str = [self.str stringByAppendingString:#"\n"];
}
When you do that the setter for the property takes care of releasing the old object in the str property before assigning a new value to the property.
Be aware, though, that assigning an object to a retained property increases it's retain count. This will create a leak:
self.str = [NSString alloc] initWithFormat: #"number %d", value];
(because all alloc/init calls return objects with a retain-count of 1)
and then the property retains it again.
That code should be written like this:
self.str = [[NSString alloc] initWithFormat: #"number %d", value] autorelease];

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

Why am I NOT getting an Exception with this getter and setter for a NSMutableString Property

I just put up a wrong answer (deleted)
The code was in response to this question. The OP wanted to know why they got a
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with setString:
(My answer was a calamity of me not paying attention to what I was doing, being tired and still learning. The usual excuses. :-) )
When they tried to set the mutable string with:
[firstName setString:#""];
The property is a NSMutableString
#property (copy,nonatomic) NSMutableString* firstName
I posted a bit of code that was working for me. But mistook the getter for the setter.
(I am still new, and tired :-) )
Now what is confusing is once it was pointed out that I was wrong. I re-looked at my code and realised what I had done.
But in my project I only had the setter synthesised and not declared.
#synthesize firstName =_firstName;
And I had declared the getter like so:
-(NSMutableString *)firstName{
if (!_firstName) _firstName = [[NSMutableString alloc]init];
return _firstName;
}
But all was working with no issue without me declaring a setter. Which your supposed to do for a property for a mutable object and (copy)
If put a setter in :
-(void)setFirstName:(NSMutableString *)mutableString{
_firstName = mutableString ;
}
It still works all ok.
I use the call:
[self.firstName setString:#"some words"];
I did get the Exception once when I think I first removed the getter and leaving the setter.
But I cannot repeat the error!
I hope this is clear..
Does any one know what is going on. And am I doing the setter and getter correctly in this case.
Thanks
The setter you wrote is incorrect for the reason that it doesn't make a mutable copy of the string which is passed in.
But I think your real question is why you don't see an exception here. The reason is: you haven't actually used the bad setter in the code you posted here.
Here is the code you posted:
[self.firstName setString:#"some words"];
This is basically the same as this:
NSMutableString *tmp = [self firstName]; // this is using your getter
[tmp setString:#"some words"]; // this is calling a method on NSMutableString
So first you use your getter, which correctly returns an NSMutableString*. Then you call a method that is already defined in Foundation called -[NSMutableString setString:]. This works fine because you are in fact sending it to an NSMutableString. So far you have avoided any issue.
Where you would have gotten an exception is something like this:
// assume myObj is an instance of whatever class has this 'firstName' property
[myObj setFirstName:#"Some static and immutable string"];
// OOPS! Now we've used the broken setter
// and firstName is now NOT a mutable string!
[myObj.firstName setString:#"Some other string"];
// ERROR. Now we've tried to send setString to an immutable string object.
I hope that helps.
BTW, it sounds like you are using ARC. In that case, a correct setter can be as simple as this:
-(void)setFirstName:(NSMutableString *)someString{
_firstName = [someString mutableCopy] ;
}
Given a property declaration:
#property (copy,nonatomic) NSMutableString* firstName;
In non-ARC code the setter should be implemented like this:
- (void) setFirstName: (NSMutableString *) newString
{
if ( _firstName != newString ) {
[_firstName release];
_firstName = [newString mutableCopy];
}
}
or like this:
- (void) setFirstName: (NSMutableString *) newString
{
[_firstName autorelease];
_firstName = [newString mutableCopy];
}
A simple assignment won't do because without copying the new value you will crash. A simple copy won't do either because the ivar is a mutable string.

NSString blowing my mind away

I have three NSString properties declared like this:
#property(nonatomic,retain) NSString *currentPassword;
#property(nonatomic,retain) NSString *newPassword;
#property(nonatomic,retain) NSString *confirmPassword;
I initialize them in a viewDidLoad method:
currentPassword = [[NSString alloc]init];
newPassword = [[NSString alloc]init];
confirmPassword = [[NSString alloc]init];
The funny thing is that they are the same object after initialize them as different objects!
Is this some kind of compiler optimization?
Thank you
Is this some kind of compiler optimization?
Not quite. It's a special case value for a constant, and an optimization of a common concrete immutable type/value which has been implemented by the NSString class.
NSString is immutable. There's no reason multiple instances of the same empty string are needed. In such simple cases, -[NSString init] can take the form:
static NSString* const EmptyNSString = #"";
- (id)init
{
self = [super init];
[self release];
return EmptyNSString;
}
similarly, + [NSString string]:
+ (id)string
{
return EmptyNSString;
}
So there are a few static immutable objects which are used this way where it makes sense. Other obvious examples include + [NSArray array] and + [NSNumber numberWithBool:].
Each one of these constants can represent what would have been many many many thousands of unique allocations produced during your program's execution.
This happens to work because NSString as a class cluster: You are returned an object of one of many opaque types which implements the interface declared by NSString. Therefore, a NSMutableString type could then implement init appropriately:
- (id)init
{
self = [super init];
if (nil != self) { ... }
return self;
}
Finally, you should in almost all cases declare your NSString properties as copy.
As NSString objects are immutable (i.e. cannot be changed after they are created) and there's no sense in creating several different instances of the same immutable strings, system tries to reuse existing objects whenever possible.
Using constructor with no parameters may be one of examples. You can also check that +stringWithString: (and -initWithString:) also return the (retained) parameter string, and copy method in NSString is equivalent to retain.
Remember that optimization is only possible because we know NSString instance is not going to change and the same tests with NSMutableString most likely will to create new string instances.
P.S. About NSAssert usage:
NSAssert Generates an assertion if a given condition is false.
So your assert condition should be reversed:
NSAssert(currentPassword && newPassword && confirmPassword,#"nil field");
When you have NSString as a property you should specify the attribute 'copy' instead.
NSString is defined as an immutable type, so whenever the compiler can optimize things by combining identical strings, it should. if you use #"myString" in two separate places in your code, they will be referencing the very same object. #"" strings are of class NSConstantString

Confused about alloc and release

I'm a little confused about Objective-C and allocating/releasing objects.
If I do this:
NSString *myString;
if([someString isEqualToString: #"test1"]){
myString = #"got 1";
}else{
myString = #"got 2";
}
Do I have to release myString after that?
And the same with self-defined objects:
myOwnObject *someObject = [someArray objectAtIndex: 1];
myButton.label1.text = someObject.name;
Do I have to release someObject?
The reason why I'm asking is that I get memory-leaks in a method and I can't find where it is. So I'm trying to figure out whether I do the alloc/release stuff correctly.
The leak occurs on a NSPlaceholderString (I guess that's somewhere hidden in my NIB-File).
Also - if I have an object, allocate it, but only use some of the properties, but DO a release of every property on dealloc - will this cause memory leaks?
Sorry - hope my questions do make at least some sense :)
Thanks for any help!
Listen to me. THIS IS THE ONLY RULE THAT MATTERS.
If you use a method with "copy", "alloc", "new", or "retain" in the name
You own the object and MUST later release or autorelease it.
If you don't:
Don't!
But don't expect the object to stick around outside of that scope, because you don't own it.
It's that simple.
MyClass *foo = [[MyClass alloc] init];
[array addObject:foo];
[foo release];
Did you use "copy", "retain", "new", or "alloc"? Yes. Release it.
MyClass *someObject = [someArray objectAtIndex:0];
Did you use "copy", "retain", "new", or "alloc"? No. Don't release it.
BUT
If you have an instance variable which you need to access in other methods:
ivar = [[someArray objectAtIndex:0] retain];
Then you're guaranteed it will stick around because you own it.
(Another way to handle this is with #property (retain) properties, because then you can do self.ivar = someObject and it'll retain it for you.)
But remember to release them in -dealloc!
No, you don't have to release either of those. I usually release only objects that I alloc, such as this snippet:
NSString *string = [[NSString alloc] initWithFormat:#"%#", something];
// yadda yadda yadda some code...
[string release];
To answer your first question, you don't need to release strings created with the #"" syntax.
On your second example, you should not have to release someObject. However, a problem could arise if your dealloc method in your myOwnObject class does not correctly release all of its instance variables.