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

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

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.

IOS Release and assignment messages differences for nsstring

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.

Lots of Problems About NSString Reference Count

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.

Understanding ARC in iOS

I have this block of code I have written to test ARC. I set the string s2 as weak and assign it the value of s1. Then, I set s1 to nil. I was assuming that since this background block is executed at a later time, s2 will already be dealloced by then. But, when I run this code, the NSLog still prints the value of s2 as "123". Can someone please explain to me why that happens?
- (void)testARC {
NSString *s1 = [NSString stringWithFormat:#"123"];
__weak NSString *s2 = s1;
s1 = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// what will NSLog print?
NSLog(#"s2 = %#", s2);
});
}
Two considerations:
You're creating an autorelease object which isn't released until the pool is drained. If you create a non-autorelease object (or use your own autorelease pool), you won't see that behavior, e.g.:
NSString *s1 = [[NSString alloc] initWithFormat:#"123"];
__weak NSString *s2 = s1;
s1 = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// what will NSLog print?
NSLog(#"s2 = %#", s2);
});
You always should be wary of evaluating memory behavior using NSString. In this case, I don't think it matters, but in other cases NSString will do all sorts of optimizations and caching that can affect the behavior. I'd suggest testing this with NSObject or an NSObject subclass.

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