Understanding ARC in iOS - 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.

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.

Are all objects in the iOS automatically added to the auto release pool?

Are all objects in the iOS automatically added to the autorelease pool?When ARC? If no, which one will be added & which one not?
eg:
{
NSString *str = [[NSString alloc] init];
NSString *str2 = [NSString string];
}
+ (NSString *)string {
return [[NSString alloc] init];
}
str2 will be added to autorelease pool because it creates by a method that name as 'copy/mutableCopy/alloc/new'. So it does not create by self. But I don't know is str will be added or not and why?
ARC makes no guarantees as to what objects are added to the autorelease pool and what objects are not. It is very possible that neither of the objects in your example are ever added to the autorelease pool under ARC.
The answer is NO. In fact, which will be added auto release pool is obj of method "string".
// ARC
{
NSString __strong *str = [[NSString alloc] init]; // STEP A
NSString __strong *str2 = [NSString string]; // STEP B
}// STEP Finish
+ (NSString *)string {
id obj = [[NSString alloc] init];
return obj;
}
Like this may help you understand.In ARC, object has __strong modifier(default).Because a object must has a strong pointer point to it, or it die.
STEP A : str create a NSString object and strong point to itself.
STEP B : str2 not create but strong point to a object which is obj.obj created by NSString in method string, because it needs return, so added to auto release pool. And str2 strong point to obj, so obj can't release by auto release pool, there still a strong pointer point to it.
STEP Finish,str,str2 out of method, no more strong pointer .So they had release.
At STEP B, why will needs auto release pool? Because if don't put obj into auto release pool, it will release by compiler, just like str in the STEP Finish.
All this from the book "Pro multithreading and memory management for iOS and OS X".
Forget my poor English.

Objective-C memory leak when returning NSString

I want to be sure that my code is not leaking, since this small snippet is called thousand times in my app. I run the app through Instruments and the initWithBytes seems to be problematic. Is anything wrong in this code?
First [reader readString] is called.
case FirstCase:
{
NSString *string = [reader readString];
[self setPropertyByName:propertyName value:string];
break;
}
...
readString is returns the strings which is autoreleased.
- (NSString*) readString
{
...
NSString *string = [[[[NSString alloc] initWithBytes:cursor length:stringLength encoding:NSUTF8StringEncoding] autorelease];
return string;
}
Is the code OK? Any other better approach to avoid autorelease?
I cannot change my code to ARC. Plain old non-ARC memory management.
What you posted is OK. The only rule at this point is that methods contain "create" or "alloc" will return an object that needs to be explicitly released. In your case that is the string returned in the readString method.
Since the object will be returned you need to retain it till the end of the run loop cycle which the autorelease pool will do. What that means for instance is if this method will be called in a for loop the objects will not be deallocated before the loop has exited.
If you want or need to avoid that I suggest you to do the same pattern with "create" or "alloc" and return an object not being autoreleased:
case FirstCase:
{
NSString *string = [reader createReadString];
[self setPropertyByName:propertyName value:string];
[string release];
break;
}
...
- (NSString*) createReadString
{
...
NSString *string = [[[NSString alloc] initWithBytes:cursor length:stringLength encoding:NSUTF8StringEncoding];
return string;
}

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.

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

Resources