NSString *string2 = string1;
That does not create a new object. string1 is not an object, it is a pointer to an object. that assignment merely makes a copy of the pointer and places it in string2.
Due to an implementation detail (an internal implementation detail that may not be true in all cases and should never be relied upon), this is true:
NSString *string1 = [NSString stringWithString:@"string"];
string1 == @"string"; // this is a true expression
String constants -- @"dddd" -- are compiled into the binary by the compiler. They can't be released and there is only one constant per unique string. stringWithString: recognizes constants and does not make a new copy.
Now, if you were to do:
NSMutableString *string1 = [NSMutableString stringWithString:@"string"];
NSMutableString *string2 = string1;
[string2 release];
That would copy the @"string" into a new instance of NSMutableString and string1 would point to that object.
That code, though, would be an over-release ([string1 release]; would be, too) since stringWithString: returns an autoreleased object.
Now, if you were to:
NSMutableString *string1 = [[NSMutableString alloc] initWithString:@"string"];
NSMutableString *string2 = string1;
[string2 release];
Then that code is technically correct in terms of the retain counts; the string object would be deallocated. But after the release, both string1 and string2 would be dangling pointers -- that is, the address they hold no longer points to a viable object and attempting to message either would yield undefined results.