I don't understand this: NSString question about what a book said:

Discussion in 'iOS Programming' started by KarlJay, Aug 18, 2012.

  1. KarlJay, Aug 18, 2012
    Last edited: Aug 18, 2012

    macrumors 65816

    Joined:
    May 1, 2010
    Location:
    California
    #1
    Book: Objective-C Developer reference, Wiley Pub, 2011, ISBN: 978-0-470-47922-3 Author: Jiva DeVoe

    Chap: 12 Page 265 'Using Strings'

    Code:
    Listing 12.2
    Examining string constant equality
    NSString *string1 = @”this is a string”;
    NSString *string2 = @”this is a string”; // same object as string1
    NSString *string3 = [NSString stringWithString:string1]; // makes new
    assert(string1 == string2); // true
    assert([string1 isEqual:string2]); // also true
    assert([string1 isEqual:string3]); // true
    assert(string1 == string3); // false
    Ok, so it says: "Any two declarations of the exact same string value, even if stored in different variable names, point to the same object."

    So I do this:
    Code:
        NSString *string1 = @"Number One";
        NSString *string2 = @"Number One";
        string2 = [string2 stringByAppendingString: @"New Text"];
        NSLog(@"String 1 %@", string1);
        NSLog(@"String 2 %@", string2);
    string1 = Number One
    string2 = Number OneNew Text

    If string1 and string2 point to the SAME object, then why when one is changed, the other doesn't change?

    And how in the world do they both point to the same object only based on string content?:confused:

    Or is it that the pointers WERE the same, and now that one string changed, the pointer changed? if so, that's just strange.

    Edit: Ok, just checked, with this code:

    They are == on the 1st if() and NOT == on the second if(), so changing a string, changes it's pointer and if two strings are the same, they'll be given the same pointer?

    Edit2:
    Ok, so what happens if I have two strings NOT the same, then within the program, they become the same, have I just lost a pointer? And what happens if that pointer was used specifically in another part or another thread of the same program? What if I store the value of a pointer to a var, the pointer then changes... does the var change?
     
  2. macrumors 68000

    Reason077

    Joined:
    Aug 14, 2007
    #2
    string1 and string2 point to the same object at compile time. So the value of (string1 == string2) will be YES.

    But when you call "string2 = [string2 stringByAppendingString: @"New Text"];" you are creating a NEW string object, and assigning that new object to the string2 variable.

    ----------

    String are only guaranteed unique at compile time. At runtime you could indeed end up with multiple NSStrings with the same content.
     
  3. macrumors Pentium

    KnightWRX

    Joined:
    Jan 28, 2009
    Location:
    Quebec, Canada
    #3
    The book doesn't make it quite clear, but it's not based on value or content, it's based on the fact you're initially pointing to what is known as a string literal, an object created by the compile based on the static string you put in your source code. Since both literals are the same, the compiler does not create 2 objects, only 1.

    When you using any NSString method that returns a new string, your pointer will obviously not point to the string literal anymore. That literal is constant and cannot be modified. Also, NSStrings cannot be modified, you have to use the NSMutableString class to get a modifiable string. Any "appends" to an NSString ([string stringByAppendingString]) returns a new pointer. You should never write code like the following :

    Code:
    string2 = [string2 stringByAppendingString: @"New Text"];
    
    That could be a memory leak.
     
  4. macrumors 68000

    Reason077

    Joined:
    Aug 14, 2007
    #4
    How? Even if not using ARC, there is no memory leak here. You're replacing an NSString constant with a autoreleased NSString.

    Without ARC, it's a bit sketchy to write code like that in case the initial value of string2 was a retained object. But with ARC it's totally fine.
     
  5. macrumors Pentium

    KnightWRX

    Joined:
    Jan 28, 2009
    Location:
    Quebec, Canada
    #5
    In this particular case, it isn't. However, getting used to writing such code if not using ARC could lead to a memory leak. The use of the conditional ("could be a memory leak") was on purpose here.

    The OP seems to be under the impression that stringByAppendingString modified the string object. It creates a new one. If you were to apply the same code to a normally init'ed string, you now have a leak. If you want a modifiable string and want to append to it, you use NSMutableString's instance method appendString or appendFormat.

    Just have to break bad habits before they form. You should never return the new string created by the method into the pointer you used to create it.
     
  6. macrumors 68000

    Reason077

    Joined:
    Aug 14, 2007
    #6
    Before ARC, I would agree with you. But there's no issue with doing this with ARC code.
     
  7. macrumors Pentium

    KnightWRX

    Joined:
    Jan 28, 2009
    Location:
    Quebec, Canada
    #7
    Sure, but if you start mixing it up, you have to think about it. "Is this project migrated to ARC yet ?" Just adopt sensible habits and you'll never run into the issue.

    Let's not encourage bad habits because the compiler fixes them for us. Let's understand the underlying concepts and write good code to begin with. That's what I prefer to pass down to up and comers. I don't see how you can disagree with that.
     
  8. macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #8
     
  9. thread starter macrumors 65816

    Joined:
    May 1, 2010
    Location:
    California
    #9
    Your right and great point. In this example I was actually wanting to compare the pointers because I was surprised that the pointers would be the same.

    Depending on what you are doing, this optimization, could hinder. In other words, if the two strings become equal and unequal over and over, thats a lot of object creation.
     
  10. chown33, Aug 18, 2012
    Last edited: Aug 18, 2012

    macrumors 603

    Joined:
    Aug 9, 2009
    #10
    You're making a simple yet profound mistake here, and it's the cause of your confusion in the original post.

    Two "strings" don't become equal or unequal, if they're immutable objects (which NSStrings are).

    Two variables might become equal or unequal, as their values change over time. You're confusing the value, which is the actual memory address of the NSString object, with the variable, which holds a value.

    A variable holds a value. It holds a new value when it's assigned a new value. It holds the last-assigned value for as long as the variable exists. The variable is not the value. The variable is simply the container that holds the value and allows you to name it in expressions.

    Simple example:
    Code:
    int a = 5;
    int b = 3 + 2;
    
    The values of a and b are equal. The variables hold the same value. The values have the same bit-pattern.

    The value of a (i.e. the value stored in a) is currently 5, but a is a variable, so that can change. It happens to match the value of b, but b is also a variable, and both variables can be assigned new values. Assigning new values may change the relationship of equality, but this should be expected since the names refer to variables, not constants. It is not the case that a will always be 5 and be will always be equal to it.

    Suppose you then have this line of code:
    Code:
    b = 9 / 3;
    
    Now a and b are not equal. The variables hold different values. The values have different bit-patterns. But this does not mean that 5 (the value in a) is now equal to 3 (the value in b). There's no surprise here, as there is in your first post. You need to look carefully at what's really happening to see why you're not surprised by the code with a and b, yet you are surprised by the code with string1 and string2.

    Go back to your first example. Consider that your variables are both pointers to an NSString object. A pointer is an address. The variables hold the same value. The values have the same bit-pattern. But what is the value, exactly? It's a memory address; a number indicating an addressable memory location where the actual object data is stored. So when the variables are equal (using ==), the bit-pattern (i.e. the number signifying a memory address) in both variables is identical.

    Now assign a different value to string2. What happens? A function/method is called, which returns a value. That value (a memory pointer) is stored into the variable. What is the memory pointer's actual numeric value? You'd have to print it as a number to see, but I can guarantee one thing: it will not be the same bit-pattern as the value stored in string1.

    If that's confusing, try using the %p format-specifier with an NSLog to print the string1 and string2 values.


    An array (a simple C array) is an example of multiple variables with a single name. Each slot of the array holds a distinct variable. You can read and write each slot separately, and the variables never overwrite one another. Each slot in the array is addressed by a name (the array name) and an index (an integer position). One name, multiple variables.
     
  11. thread starter macrumors 65816

    Joined:
    May 1, 2010
    Location:
    California
    #11
    I don't think I was confused about the pointer to a string vs the string itself. I was confused about the "when the strings are the same, the pointers change to be the same"

    Ok, let me try this:

    str1 (a pointer to a string)
    str2 (a pointer to a string)

    "one two three" (a string)

    str1 = "one two three" // str1 is a pointer to a string, the string is "one two three"

    str2 = "abcdef" // str2 is a different pointer to a different string

    str1 == str2 // NO pointers are NOT the same

    str2 = "one two three" // str2 now has a different string that it had before

    Now:
    str1 is a pointer to a string "one two three"
    str2 is a pointer to a string "one two three"

    The pointers before were not equal and their string values were not equal.
    the string value of str2 changed and is now equal to the string value of str1

    The pointers were different, now the pointers are the same.

    changing the value of a string CAN change it's pointer value ONLY when the value of the string matches the value of another string.

    This is the part that I find confusing.
     
  12. chown33, Aug 18, 2012
    Last edited: Aug 18, 2012

    macrumors 603

    Joined:
    Aug 9, 2009
    #12
    You wrote pseudo-code, so I don't know how to answer. If you want to try writing real C or Objective-C, I can tell you whether the pointers will be the same or not.

    Or you can write the code, compile it, use the %p format-specifier, and see for yourself if the pointers are the same.


    So what? Describe how this is a problem.


    I don't understand what you mean by the underlined statement.

    You're using terms like "the value of a string" in an unclear way. Do you mean "the characters stored at a particular memory address"? Because nothing is changing any characters stored at any memory address. A variable is changing, and it points to an object. But no objects have any of their contents changed.

    I don't see what's confusing here. The compiler is simply making a safe optimization, by seeing that two variables refer to the same immutable object, which is an NSString object containing the characters sequence "one two three".

    Suppose the compiler didn't do this, what would happen? You'd have separate two objects, each with the contents "one two three". The contents would be the same, so isEqual: would return YES, but the pointers would be different (two separate objects), so == would be false.

    The compiler is only permitted to do this because an NSString is immutable. That NSString's contents, i.e. the sequence of characters "one two three", is immutable. If the contents could be changed in the same object, then the compiler couldn't do this optimization. "In the same object" means the object pointer is the same before and after the change. Only NSMutableString is mutable, though, and no @"" literal is an NSMutableString.


    In this Objective-C:
    Code:
    NSString * str1 = @"one two three";
    NSString * str2 = @"five";
    
    There are two NSString literal objects (note the terminology: two objects). There are also two variables, and each variable holds a pointer to a different object. The two bit-patterns will be different, because the memory at a single address can only hold one thing at a time.

    Now consider this code:
    Code:
    NSString * str1 = @"one two three";
    NSString * str2 = @"one two three";
    
    There is one NSString literal object. There are two variables, and they both have the same value: the address of the single NSString literal. The two bit-patterns in the variables will be the same, because they both point to the same NSString object. The compiler can do this because it sees it's already defined an NSString object with contents "one two three", so it simply reuses the same object for str2.

    Since the object is immutable (as all NSStrings are), there is no harm that can arise from sharing the single string object. No code can change the object and cause any other code to suddenly have a string that isn't "one two three". That's because no code can change the object at all: it's an immutable object.

    That literal NSString object will never change. Its memory is fixed. It will never be dealloc'ed and used to make another object, so its memory address will never be used for any other object. Since only one thing can occupy a given memory address at a time, there is zero chance that something else will appear instead of that object.

    The code can make new objects, and assign those object pointers to existing variables, e.g. assign a new value to str2. But that's obviously assigning a new value to a variable. It has no effect at all on the contents of the literal NSString object, and also has no effect on the value of the variable str1.

    If you assign another object pointer to str1, you'll have no pointers left in any variable that point to the literal NSString object. It still won't be reclaimed (dealloc'ed), which might technically make it a memory leak, but it's not a recurring leak so it's not a problem.


    I still think you should just write some real code, use %p to see pointer values, and see what happens. If you write something you don't understand, then post the code, describe what you expected to happen, describe what actually happened, and we'll try to explain.

    I think if you look at it in terms of variables that hold values, and those values are addresses of objects in memory, it will eventually make sense. You need to see some real addresses, i.e. real hex numbers, to see what happens.
     
  13. thread starter macrumors 65816

    Joined:
    May 1, 2010
    Location:
    California
    #13
    I think this whole thing just sunk in with your last post.

    The problem I was having was the usage of string. ObjC has NSString and NSMutableString. If I do the exact same thing using NSMutableString, I should get the results I expected.

    I wasn't thinking of NSString as a literal object or fixed object.

    Thanks!
     

Share This Page