IOS Release and assignment messages differences for nsstring - ios

I was going through the memory management concepts. I created one string1 and assign that string1 into another string2, now I release this string1.
Here string2 retain count is 1 but on NSLog statement it gives EXC Bad access.
When I am assigning the string
NSString * string1 = [[NSString alloc]initWithFormat:#"hello"];
string2 = string1;
NSLog(#"string1 memory address = %p, string2 memory address = %p", &string1, &string2);
[string1 release];
NSLog(#"[string2 retainCount] = %lu", (unsigned long)[string2 retainCount]);
NSLog(#"string2 = %#", string2); // here app is crashing
Does it means that string2 has an autorelease message also with it because if I do string2 = [string1 copy]; instead of string2 = string1; it doesn't crash.
So I wanted to ask whether the crash is because it has autorelease message of string2 and how it is relating with string2 release command.
Please advice!

Assignment doesn't change object's retain count if you use manual memory management in Objective-C. And you for sure use it, otherwise, you can't invoke release method in your code.
So, your code does the following. It creates NSString object with retain count = 1, and assigns it to string1 pointer. After that, you assigns string1 to string2. Now you have 2 pointers to the same object, and retain count of this object is still 1. Then you release object, it deallocated immediately. And after that you experiencing crash:
NSString * string1 = [[NSString alloc]initWithFormat:#"hello"]; // string retain count is 1
string2 = string1; // 2 pointers to same string, retain count is still 1
[string1 release]; // string is deallocated when retain count drops to 0
NSLog(#"string2 = %#", string2); // here app is crashing
To fix that, you can use retain when you do an assignment.
NSString * string1 = [[NSString alloc]initWithFormat:#"hello"]; // string retain count is 1
string2 = [string1 retain]; // 2 pointers to same string, retain count is 2
[string1 release]; // string retain count back to 1
NSLog(#"string2 = %#", string2); // no crash
Also, you can use copy. Note that for NSString copy doesn't actually copies an object, it simply invokes retain. There is no need to perform actual copying, because NSString is immutable and can't be changed. If we will use NSMutableString, things will change:
NSMutableString * string1 = [[NSMutableString alloc]initWithFormat:#"hello"]; // string retain count is 1
NSMutableString * string2 = [string1 copy]; // 2 separate strings, both have retain count 1
[string1 release]; // string1 is deallocated
NSLog(#"string2 = %#", string2); // no crash, string2 retain count is 1
Alternatively, you can use ARC. It will insert corresponding retain/release calls at compile time. Code then will look like:
NSString * string1 = [[NSString alloc]initWithFormat:#"hello"];
string2 = string1;
string1 = nil;
NSLog(#"string2 = %#", string2); // no crash
I suggest to understand manual memory management first, and after that migrate to ARC.

Related

A developer when going through a code base encounters two different syntaxes for creating a NSString with the given integer. (implemented in MRC)

1. NSString *str1 = [NSString stringWithInt: 10];
2. NSString *str2 = [[NSString alloc] initWithInt: 10];
What is the difference between these methods?
The history goes to times when automatic reference counting doesn't existed.
Then Apple have created notation, that methods with prefixes init, copy, new returns owned pointer, that mean that you don't need to retain it, other methods should return autoreleased pointers. For example
NSString *a;
NSString *b;
#autoreleasepool {
a = [[NSString alloc] initWithFormat:#"%d", 1];
b = [NSString stringWithFormat:#"%d", 2];
NSLog(#"String a in autorelease pool is %#", a); //output: String a in autoreleasepool is 1
NSLog(#"String b in autorelease pool is %#", b); //output: String b in autoreleasepool is 2
}
NSLog(#"String a out of autorelease pool is %#", a); //output: String a out of autorelease pool is 1
NSLog(#"String b in autorelease pool is %#", b); //crash: referencing invalid object, because b was autoreleased but variable `b` still pointing at released object
For proper working outside autoreleasepool variable b should be retained:
NSString *b;
#autoreleasepool {
b = [[NSString stringWithFormat:#"%d", 2] retain];
NSLog(#"String b in autorelease pool is %#", b); //output: String b in autoreleasepool is 2
}
NSLog(#"String b out of autorelease pool is %#", b); //output: String b out of autorelease pool is 1
But if you use retain properties, you will face opposite situation: memory leaks.
Proper code will be
#property (retain) NSString *a;
#property (retain) NSString *b;
#autoreleaspool {
self.a = [[[NSString alloc] initWithFormat:#"%d", 1] autorelease];
self.b = [NSString stringWithFormat:#"%d", 2];
}
So exact implementation of stringWithFormat: is doing alloc+init+autorelease
Of course now when we have ARC we don't need think about proper object retaining and releasing.
In ARC first code snipped will work fine without crashes, second code snippet will work fine without memory-leaks.
So now it's just for programmer sense which one he prefer most. It's like in swift: you can write: SomeClass(...) or SomeClass.init(...)
The first is a class factory method. See http://www.apeth.com/iOSBook/ch04.html#_class_methods_2
The second is a genuine initializer.
The difference between them was much more obvious back in the day of manual memory management. Now that ARC exists, they are indistinguishable — which is why Swift eliminates the former.

Need assistance regarding NSString

In NSString NSString Class Reference what this means
Distributed objects:
Over distributed-object connections, mutable string objects are passed by-reference and immutable string objects are passed by-copy.
And NSString can't be changed, so what happening when I am changing str in this code
NSString *str = #"";
for (int i=0; i<1000; i++) {
str = [str stringByAppendingFormat:#"%d", i];
}
will I get memory leak? Or what?
What your code is doing:
NSString *str = #""; // pointer str points at memory address 123 for example
for (int i=0; i<1000; i++) {
// now you don't change the value to which the pointer str points
// instead you create a new string located at address, lets say, 900 and let the pointer str know to point at address 900 instead of 123
str = [str stringByAppendingFormat:#"%d", i]; // this method creates a new string and returns a pointer to the new string!
// you can't do this because str is immutable
// [str appendString:#"mmmm"];
}
Mutable means you can change the NSString. For example with appendString.
pass by copy means that you get a copy of NSString and you can do whatever you want; it does not change the original NSString
- (void)magic:(NSString *)string
{
string = #"LOL";
NSLog(#"%#", string);
}
// somewhere in your code
NSString *s = #"Hello";
NSLog(#"%#", s); // prints hello
[self magic:s]; // prints LOL
NSLog(#"%#", s); // prints hello not lol
But imagine you get a mutable NSString.
- (void)magic2:(NSMutableString *)string
{
[string appendString:#".COM"];
}
// somewhere in your code
NSString *s = #"Hello";
NSMutableString *m = [s mutableCopy];
NSLog(#"%#", m); // prints hello
[self magic2:m];
NSLog(#"%#", m); // prints hello.COM
Because you pass a reference you can actually change the "value" of your string object since you are working with the original version and not a duplicate.
NOTE
String literals live as long as your app lives. In your exmaple it means that your NSString *str = #""; never gets deallocated. So in the end after you have looped through your for loop there are two string objects living in your memory. Its #"" which you cannot access anymore since you have no pointer to it but it is still there! And your new string str=123456....1000; But this is not a memory leak.
more information
No, you will not get memory leak with your code, as you are not retaining those objects in the loop, they're created with convenience method, you don't own them, and they will be released on next cycle of autorelease pool. And, it's doesn't matter if you are using ARC or not, objects created with convenience methods and not retained are released wherever they are out of their context.
In will not leak memory, but will get more memory allocation, due to making new copy of immutable copy as many time loop triggers [str stringByAppendingFormat:#"%d", i];.
Memory leak will get performed when, you put your data unreferenced, or orphan, this will not make your last copy of string orphan every time when loops, but will clear all copies of NSString when operation get complete, or viewDidUnload.
You will not get a memory leak in the example code because Automatic Reference Counting will detect the assignment to str and (automatically) release the old str.
But it would be much better coding style (and almost certainly better performance) to do this:
NSMutableString* mstr = [NSMutableString new];
for(int i = 0; i < 1000; ++i){
[mstr appendFormat:#"%d",i];
}
NSString* str = mstr;
...
As to the first question, I think it means that a change made to a mutable string by a remote process will be reflected in the originating process's object.

What are the retain count of NSStrings allocations mentioned below

Here I allocated NSString variables ten different ways, and I want to know the retain count for all of them.
#interface SomeClass : NSObject
{
NSString *str1;
NSString *str2;
}
#property (nonatomic, retain) NSString* str1;
#property (nonatomic, copy) NSString * str2;
- str1 =#"hello";
- self.str1 = #"hello";
- str1 = [[NSString alloc]init];
- self.str4 = [[NSString alloc]init];
- str1 = [[[NSString alloc]init]autorelease];
- self.str1 = [[[NSString alloc]init]autorelease];
- str1 = [[NSString alloc]initWithString:#"hello"];
- self.str1 = [[NSString alloc]initWithString:#"hello"];
- str1 = [[[NSString alloc]initWithString:#"hello"]autorelease];
- self.str1 = [[[NSString alloc]initWithString:#"hello"]autorelease];
What are the retain count of NSString allocations mentioned above? How can I know the retain count of them are retain count different for all of them?
While this seems like a homework assignment, you can call retainCount on each string to get an approximation of the real value. You should absolutely not use this method for any logic in a production app (see http://whentouseretaincount.com)! The documentation states:
Special Considerations
This method is of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method.
I assume they are accessed in some SomeClass method. Variants:
// replace str1 with str2(copy), retain count will remain the same
str1 = #"hello";
self.str1 = #"hello"
str1 = [[NSString alloc]initWithString:#"hello"];
self.str1 = [[NSString alloc]initWithString:#"hello"];
str1 = [[[NSString alloc]initWithString:#"hello"]autorelease];
self.str1 = [[[NSString alloc]initWithString:#"hello"]autorelease];
Here you'll end up with a huge value, like UINT_MAX, compiler will optimize your code (you pass literal value, NSString is immutable) and those objects will be unreleasable.
self.str1 = [[NSString alloc] initWithFormat:#"a string %d", 5]; // with autorelease or not - the same
Here you'll end up with a release count = 2, you alloc string +1, you assign a retain property +1 = 2.
self.str2 = [[NSString alloc] initWithFormat:#"a string %d", 5]; // with autorelease or not - the same
Here you'll end up with a release count = 1, you alloc string +1, you assign a copy proprty, thus create a copy of created string = 1.
In all other situations you'll end up with release count = 1, autorelease does not add to retain count, it just decrements it by 1 when the pool drains.
Just remember:
Do not rely on retainCount,
When you create object via alloc, new, copy, mutable copy - it's your responsibility to release it. If you create object with like [NSString string] it will be autoreleased.
retain property retains object, copy property copies object, properties are usually used via dot notation (self.property etc.) (there's also set%Property% and %property% methods synthesized, so self.property = ... is (usually) the same as [self setProperty:...])
It's time to move to ARC. So if you can you should.

Objective-C: Weak attritube don't work as expected [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why do weak NSString properties not get released in iOS?
I'm a newbie to Objective C and I've got some questions that I cannot answer myself.
I have a block of code for testing __weak variable (I'm using ARC, of course):
NSString *myString = [[NSString alloc] initWithFormat:#"John"];
NSString * __weak weakString = myString;
myString = nil; //<-- release the NSString object
NSLog(#"string: %#", weakString);
The output of the above codes is as expected, since weakString is a weak variable :
2013-01-02 11:42:27.481 ConsoleApp[836:303] string: (null)
But when I modified the code to this:
NSString *myString = [[NSString alloc] initWithFormat:#"John"];
NSString * __weak weakString = myString;
NSLog(#"Before: %#", weakString); //<--- output to see if the __weak variable really works.
myString = nil;
NSLog(#"After: %#", weakString);
The output is totally not what I expected:
2013-01-02 11:46:03.790 ConsoleApp[863:303] Before: John
2013-01-02 11:46:03.792 ConsoleApp[863:303] After: John
The output of the latter NSLog must have been (nil) instead of "John". I've tried to search in many documents but I haven't found the answer for this case.
Can someone give an reasonable explaination? Thanks in advance.
The NSLog function is retaining the passed NSString in an autorelease pool. The zeroing-weak variable will therefore not be zeroed until the autorelease pool has drained. For example:
__weak NSString* weakString = nil;
#autoreleasepool {
NSString* myString = [[NSString alloc] initWithFormat:#"Foo"]; // Retain count 1
weakString = myString; // Retain count 1
NSLog(#"A: %#", weakString); // Retain count 2
NSLog(#"B: %#", weakString); // Retain count 3
myString = nil; // Retain count 2
NSLog(#"C: %#", weakString); // Retain count 3
NSAssert(weakString != nil, #"weakString is kept alive by the autorelease pool");
}
// retain count 0
NSAssert(weakString == nil, #"Autorelease pool has drained.");
Why is NSLog putting the string into an autorelease pool? That's an implementation detail.
You can use the debugger or Instruments to follow the retain count of the NSString instance. The exact retain counts are unimportant, but it does shed some light as to what's going on behind the scenes. What is important is that the NSString instance is deallocated when the autorelease pool is drained.
I think it’s just some implementation detail. Your weak variable is getting cleared, but not just immediately. For example, this works as expected:
NSString *myString = [[NSString alloc] initWithFormat:#"John"];
NSString * __weak weakString = myString;
#autoreleasepool {
NSLog(#"Before: %#", weakString);
myString = nil;
}
NSLog(#"After: %#", weakString); // nil

Over-release an object and the app does not crash

#property (retain) NSString *testString;
self.testString = [[NSString alloc] initWithString:#"aaa"];
[self.testString retain];
self.testString = [NSString stringWithString:#"a"];
[self.testString release];
[self.testString release];
Let's go line by line:
Line 2: retain count of testString = 2
Line 3: retain count of testString = 3
Line 4: retain count of testString = 1
Line 5: retain count of testString = 0
Line 6: it should crash
Even if there's other stuff holding to testString in CoreFoundation, it eventually will go away. But the app never crash due to this.
Anyone could explain this? Thanks!
see this code and its log:
NSString *string1 = [NSString stringWithString:#"a"];
NSString *string2= #"a";
NSLog(#"String1: %p", string1);
NSLog(#"String2: %p", string2);
2012-03-22 13:21:49.433 TableDemo[37385:f803] String1: 0x5860
2012-03-22 13:21:49.434 TableDemo[37385:f803] String2: 0x5860
as you see [NSString stringWithString:#"a"]; doesn't create a new string, it uses the string literal #"a". And string literals can't be deallocated.
Try your code with NSMutableString and you will see a crash.
I am not an expert about this, so please take this with a grain of salt. I guess that [NSString stringWithString:#"a"] will probably just return the literal string #"a", i.e. it just returns its argument. As #"a" is a literal, it probably resides in constant memory and can't be deallocated (so it should be initialized with a very high retain count).
[NSString stringWithString:#"a"] returns an autoreleased object. That means "the true" retain count is 2 not 1 in line 4. In line 6 you are overreleasing your variable but the crash will happen later - on the autorelease pool drain.

Resources