Search code examples
iosobjective-cmemory-managementnsstringmanual-retain-release

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!


Solution

  • 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.