Lots of Problems About NSString Reference Count - ios

I have some tests about Mannul Reference Counting in Objective-C.
I run these code below:
- (void) test {
NSData *a1 = [[NSData alloc] init];
NSLog(#"NSData: %lu", [a1 retainCount]);
NSMutableData *a2 = [[NSMutableData alloc] init];
NSLog(#"NSMutableData: %lu", [a2 retainCount]);
NSObject *a3 = [[NSObject alloc] init];
NSLog(#"NSObject: %lu", [a3 retainCount]);
NSString *b1 = [[NSString alloc] initWithFormat: #"%#", #"ok"];
NSLog(#"NSString: %lu", [b1 retainCount]);
NSString *b2 = [[NSString alloc] initWithFormat: #"%#", a3];
NSLog(#"NSStinrg: %lu", [b2 retainCount]);
NSString *a = #"abc";
NSString *b = #"abc";
NSLog(#"%p", a);
NSLog(#"%p", b);
}
The Console :
2016-06-23 16:15:50.490 text[38147:3406036] NSData: 18446744073709551615
2016-06-23 16:15:50.491 text[38147:3406036] NSMutableData: 1
2016-06-23 16:15:50.491 text[38147:3406036] NSObject: 1
2016-06-23 16:15:50.491 text[38147:3406036] NSString: 18446744073709551615
2016-06-23 16:15:50.491 text[38147:3406036] NSStinrg: 1
2016-06-23 16:15:50.491 text[38147:3406036] 0x10359d160
2016-06-23 16:15:50.491 text[38147:3406036] 0x10359d160
And I want know why the reference counts of NSData and NSString are UINT_MAX(-1), and initWithFormat will make b2 add a count? Why are the addresses of a and b same?
Thx a lot.

Empty immutable NSDatas are uniqued to a single instance, which cannot be retained or released (indicated by the UINT_MAX-1 refcount). For the NSString one, %# is replaced by the result of calling -description on the object, and the -description method of NSString returns self. So you're getting a constant NSString literal there, which is similarly not something that can be retained or released. Constant strings are also uniqued, so there's a single one embedded in your binary at compile time, and no allocations are made.

From the apple docs
- (NSUInteger)retainCount
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.

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.

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

Why is autorelease object still alive?

I've created autorelease pool. localString has added to this pool. I released the pool. localString and string must be deallocated. But in reality they are still alive. You can see my log:
Why is the string object still alive? I don't know.
and code:
-(NSString*) happyString
{
NSString *localString = [[[NSString alloc] initWithString:#"I don't know."] autorelease];
return localString;
}
-(IBAction) onButton:(id)sender
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *string = [self happyString];
[pool release];
NSLog(#"Why is the string object still alive? %#", string);
}
Strings (NSString instances and statically allocated strings with #"") are immutable in Cocoa, so when you try to create a new NSString from a statically allocated one, the NSString class can make an optimisation: a new NSString instance is not created (the object created when you called -alloc is immediately released), and the reference to your statically allocated string is returned. That is, the line:
NSString *localString = [[[NSString alloc] initWithString:#"I don't know."] autorelease];
Is actually equivalent to:
NSString *localString = #"I don't know.";
(If you check the memory addresses of those two objects, you can see that they are the same.)
As this type of string cannot be released, it does not disappear when you expect it to.
If you were to create your string in a way that cannot be optimised, for example:
NSString *localString = [[[NSString alloc] initWithFormat:#"%#", #"I don't know."] autorelease];
Then your code will behave as you expect, and your application will (hopefully) crash at your NSLog line.
If you have tried any classes (any custom classes) other than NSString , then it would not be alive..

Can you send retain counts to NSLog to aid learning?

Just curious if there is anyway to display an objects retain count using NSLog. I just want to print them out to console to help learn how retain/release is working in some simple code?
cheers -gary-
Not only is it possible, it's very easy too:
NSLog(#"retain count=%d",[obj retainCount]);
I think you might be hitting an issue with NSString where retain and release messages can be sent to a string constant, but they actually have no effect nor alter the objects retainCount. The code below works, change it to use NSString and retain / release have no effect.
Code:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableString *myString = [[NSMutableString alloc] initWithString:#"AB"];
NSLog(#"RC: %d", [myString retainCount]);
[myString retain];
NSLog(#"RC: %d", [myString retainCount]);
[myString release];
NSLog(#"RC: %d", [myString retainCount]);
[myString release];
[pool drain];
return 0;
}
Output:
Running…
TESTBED[12306:a0f] RC: 1
TESTBED[12306:a0f] RC: 2
TESTBED[12306:a0f] RC: 1
gary
In the debugger console, you could type: print (unsigned int)[thing retainCount]

Resources