NSString Address issue - objective-c

I'm trying to print address to string but i'm getting different address in first NSLog & same address in second NSLog. So could you tell me how is this happing. Its really confusing me. Thanks a lot in advance for your efforts.
NSString *str1 = [[NSString alloc] init];
NSString *str2 = [[NSString alloc] init];
NSString *str3 = [[NSString alloc] init];
NSLog(#"str1 = %p , str2 = %p, str3 = %p",&str1,&str2,&str3);
NSLog(#"str1 = %p , str2 = %p, str3 = %p",str1,str2,str3);
Output
str1 = 0x7fff565b9c88 , str2 = 0x7fff565b9c80, str3 = 0x7fff565b9c78
str1 = 0x10c0a7060 , str2 = 0x10c0a7060, str3 = 0x10c0a7060
I don't understand why str1, str2, and str3 all point to the same memory location.

And why should str1, str2, str3 all reside at different memory addresses? They're all the same immutable string.
See bbum's comment here:
Right... one implementation detail of relevant interest (but, by no means, invalidates the answer in anyway); [[NSString alloc] initWithString:#"Hello world"] won't actually create a string on the heap. It'll just return the __NSCFConstantString (or whatever it is called) that was laid down in the mach-o file by the compiler. It is merely an interesting detail in that it does not change anything about your consumption of said string; it should be treated just like any other object.
Emphasis mine.
What's going on here is that when the compiler can determine at compile time what an immutable NSString object will be, it's creating that string differently. As bbum states, ultimately it's an implementation detail that you shouldn't worry about when you're writing your program.
But the side effect of this means that the compiler is able to make my program more memory efficient because it is able to find all of these instances and make all of my NSString pointers that it knows are supposed to be holding the same immutable value all point to the same single memory address.
We can probably achieve the same result with the following:
NSString *str1 = [[NSString alloc] init];
NSString *str2 = [NSString new];
NSString *str3 = [[NSString alloc] initWithString:#""];
NSString *str4 = [NSString stringWithString:#""];
NSString *str5 = #"";
These are all effectively the same thing.
However, if we create another string:
NSString *str6 = [NSString stringWithFormat:#"%#", #""];
This will (most likely... last time I checked) end up with a different value if we print str6 as a pointer.
And there are other ways to generate immutable NSString objects that don't get optimized like this at compile time. The point here is that if the compile can know at compile time what the string is going to be, it will create a __NSCFConstantString in the background that's outside of memory management, and it will point to that single instance whatever it can. Once it gets to run time, it will only point anything else to this if you point it there directly (str6 = str1). Otherwise, it's not going to waste execution time trying to determine if the strings are equal. If a new NSString happens to be equal and it wasn't happened at compile time, it will just be handled by ARC.
The compiler isn't able to determine that str6 is the same immutable string as the others. This is only a build time implication that the others all ended up with the same address.
Another interesting thing to note is that you will never see dealloc called on the __NSCFConstantString the compiler is creating for the variables declared in the way you declared them. So the compiler is not only making your code more efficient from a memory stand point, it's also removing all of the memory management code involved in keeping up with these strings.

The first call to NSLog is printing the addresses of the 3 local variables str1, str2 and str3. They are all residing on the stack because they are local, hence the large addresses.
The second NSLog call is printing the addresses of the objects pointed to by str1, str2 and str3, which in this case has been optimised to the same object.
Your local variables are already pointers to NSStrings, not actual NSStrings themselves, so you don't need the address operator &.

Thats interesting that you got the result but makes sense str1, str2, str3 are all immutable and so they will never change and all have the same contents, so instead of getting 3 new NSString you have just got the same one three times. If you change them to NSMutableStrings you will get the result you expected. I played around with this a little more and I found if you turn of ARC and add this
NSLog(#"str1 = %lu , str2 = %lu, str3 = %lu",
[str1 retainCount], [str2 retainCount], [str3 retainCount] );
you get some more interesting stuff, I was expecting to see the value 3, three times to represent the three allocs for the same object but instead you get 18446744073709551615, which makes sense when you also add this
NSLog(#"str1 = %# , str2 = %#, str3 = %#",
[str1 class], [str2 class], [str3 class] );
You will see the class is __NSCFConstantString which is not reference counted its the equivalent to c literal string.
You can get a similar thing with a lot of c compilers where if you define a literal c string like
char * cstr1 = "abc";
char * cstr2 = "abc";
printf( "cstr1 == cstr2 = %s\n", cstr1 == cstr2 ? "true" : "false" );
You can see the compiler has save memory for literals by only having one "abc" and pointing all pointers to the same place.
Thinking about it a little more the init method for NSString may look something like this
- (instancetype)init {
[self release];
return #"";
}

str1 is the memory address of the string
&str1 is the memory address of the pointer to the string
(could be the other way round)

Related

How to replace a char in an an char array? Xcode

i got the following char array in Objective-C (Xcode):
char *incomeMessage;
NSString *str = [[NSString alloc] initWithBytes:data.bytes length:data.length encoding:NSUTF8StringEncoding];
incomeMessage = [str UTF8String];
NSLog(#"%c", incomeMessage[0]);
NSLog(#"%c", incomeMessage[1]);
NSLog(#"%c", incomeMessage[2]);
NSLog(#"%c", incomeMessage[3]);
NSLog(#"%c", incomeMessage[4]);
NSLog(#"%c", incomeMessage[5]);
For example I get some results like this in console:
"3
2
6
1
8
4"
Now i want to replace the char in incomeMessage[2] by 4:
incomeMessage[2] = '4';
But then it gives me the error:
EXC_BAD_ACCESS
Do you have an idea, how to solve the problem?
According to the reference documentation, UTF8String returns a read-only (const char*) reference to the string data.
The reference material goes on to note:
This C string is a pointer to a structure inside the string object,
which may have a lifetime shorter than the string object and will
certainly not have a longer lifetime. Therefore, you should copy the C
string if it needs to be stored outside of the memory context in which
you use this property.
So I'd suggest following their advice and creating a copy of the array and then performing your modifications against that.
For example: http://ideone.com/mhjwZW
You might have better luck with something like:
NSString* str = [[NSString alloc] initWithBytes:data.bytes length:data.length encoding:NSUTF8StringEncoding];
char* incomeMessage = malloc([str lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1);
strcpy(incomeMessage, [str UTF8String]);
//now you can change things
incomeMessage[2] = '4';
//do this when you're done
free(incomeMessage);
Although, is there any particular reason why you want to use a C-string/character array as opposed to an NSMutableString? I think you might find replaceCharactersInRange:withString: a better approach generally. See also: stringByReplacingCharactersInRange:withString:.
i got the following char array in Objective-C (Xcode)
You don't, you know. All you have is a pointer. You have not set aside any actual memory; there is no array there.
incomeMessage = [str UTF8String];
All you've done in that line is repoint the pointer incomeMessage at your string's UTF8String. A string's UTF8String is immutable. Note this passage in the docs:
you should copy the C string if it needs to be stored outside of the memory context in which you use this property.
So basically, if you want to write into an array of char, your first task should be to make an array of char.

Weird error with NSString: No known class method for selector 'stringWithBytes:length:encoding:'

I am attempting to use scanf to assign a value to an NSString, as per the answers to this question by Omar. This is the code, taken straight from progrmr's answer:
char word[40];
int nChars = scanf("%39s", word); // read up to 39 chars (leave room for NUL)
NSString* word2 = [NSString stringWithBytes:word
length:nChars
encoding:NSUTF8StringEncoding];
However, I'm getting an error on the last line that makes absolutely no sense to me:
No known class method for selector 'stringWithBytes:length:encoding:'
What in the world could be causing this error?
And yes, I do have #import <Foundation/Foundation.h> at the top of the file.
NSString does not have a stringWithBytes:length:encoding: class method, but you can use
NSString* word2 = [[NSString alloc] initWithBytes:word
length:nChars
encoding:NSUTF8StringEncoding];
Note however, that scanf() returns the number of scanned items and
not the number of scanned characters. So nChars will contain 1 and not the string length, so you should set nChars = strlen(word) instead.
A simpler alternative is (as also mentioned in one answer to the linked question)
NSString* word2 = [NSString stringWithUTF8String:word];
NSString does not respond to the selector stringWithBytes:length:encoding:. You probably wanted initWithBytes:length:encoding:.
Story in short: you might want to consider a const char C-string suitable initializer for your NSString object. Also, allocate memory before sending any initializer message to the NSString object. I would expect something like:
char word[40];
int nChars = scanf("%39s", word);
NSString *word2 = [[NSString alloc] initWithCString:word encoding:NSASCIIStringEncoding];
Note that initWithCString per design only supports properly null '\0' terminated 8-bit character arrays. For unterminated bytes arrays you have initWithBytes:length:encoding: instead.
For Unicode characters you could consider initWithCharactersNoCopy:length:freeWhenDone:.

NSString not mantain value after "blanking" of a source NSMutableString

this is a more theoretical question than pratical;
Assuming that i have two Strings, one normal and one mutable, if i do:
NSMutableString *prova = [[NSMutableString alloc] init];
NSString *prova2 = [[NSString alloc] init];
[prova appendFormat:#"%#",#"CIAO"];
prova2 = prova;
NSLog(#"String: %#",prova);
NSLog(#"String2: %#",prova2);
[prova setString:#""];
NSLog(#"-String: %#",prova);
NSLog(#"-String2: %#",prova2);
Then the result is:
2013-04-22 22:01:53.604 CodeTest[6974:303] String: CIAO
2013-04-22 22:01:53.605 CodeTest[6974:303] String2: CIAO
2013-04-22 22:01:53.605 CodeTest[6974:303] -String:
2013-04-22 22:01:53.606 CodeTest[6974:303] -String2:
Why does it behave like this?
I just wanna find it out, because i've never encountered such thing before while programming in python, php or C++ (I think i was never confronted with so many different data types as in Obj-c :P )
Thanks
Just in case anyone wants to know how to get over it here is the right code i used to assign the value to the string without losing it after the "blanking" (sostitute to prova2 = prova):
prova2 = [NSString stringWithString:prova];
Remember that the NSString and NSMutableString variables are pointers. That means when you assign prova2 = prova you're really changing prova2 to point to prova, and the value you assigned to prova2 is lost.
So when you assign the empty string to prova both variables point to #"".
prova2 = prova;
This doesn't assign the contents of the string in prova to the string in prova2, it just makes prova2 point to the same object as prova, effectively leaking the string that prova2 used to point to. So even though prova2 is declared as a NSString*, it now points to a NSMutableString.
In addition to Richard Brown's great response, that's what you'd like to do instead for desired behaviour:
[prova2 setString: prova];
instead of
prova2 = prova;

Something is different between #"" and #"123"?

when I test method "retainCount", I met a question, just as below:
NSString *s_afmt0 = [[NSString alloc] initWithFormat:#""]; //-1
NSString *s_afmt1 = [[NSString alloc] initWithFormat:#"123"]; //1
NSLog(#"s_afmt0:%d", [s_afmt0 retainCount]);
NSLog(#"s_afmt1:%d", [s_afmt1 retainCount]);
result :
s_autf0:-1
s_autf1:1
I don't know why? why s_afmt0's retainCount is -1, and s_autf1's retainCount is 1. What is the difference between #"" and #"123"? anyone can explain ? thanks
you're printing the retainCount as a signed integer, when in fact it is an unsigned integer (type NSUInteger with a format specifier of %u, not %d). -1 is equivalent to UINT_MAX, and means the object has no retainCount. It is likely that NSString has recognized that the given string is immutable, and assigned the pointer to the static #"" empty NSString, rather than placing one on the heap.
In fact, you can confirm this with the following code:
NSString* str1 = [[NSString alloc] initWithFormat:#""];
NSString* str2 = [[NSString alloc] initWithFormat:#""];
NSLog(#"string1: %p, string2: %p", str1, str2);
The above code should print the same pointer address for both str1 and str2, confirming my theory that NSString optimizes this particular case out, and indeed it does:
06:46:30.142 StringTest[45214:303] string1: 0x7fff7d8acf90, string2: 0x7fff7d8acf90
So let's see if this is specific to NSString. It should be, otherwise all NSMutableString objects initialized to an empty string would point to the same memory!
NSMutableString* str1 = [[NSMutableString alloc] initWithFormat:#""];
NSMutableString* str2 = [[NSMutableString alloc] initWithFormat:#""];
NSLog(#"string1: %p, string2: %p", str1, str2);
Outputs:
06:53:36.688 StringTest[45278:303] string1: 0x10010a850, string2: 0x10010a890
Two different memory locations. So there you have it, a neat little optimization in the Foundation framework. TIL
Edit:
As bbum points out, you should never rely on retainCount, and this is an example of where things can go wrong if you do
While you should never use retainCount, the most likely reason for what you are seeing is that the empty string #"" is being treated special by the compiler since it is such a common literal.
Also, why are you using string formats with string literals? I suppose this is just for testing purposes but you should just have:
NSString *s_afmt0 = #""; // no need to use stringWithFormat here

EXC_BAD_ACCESS memory issue when accessing instance variables

I'm having issues porting a Java project I worked on a while ago to Objective-C code - I get "Program received signal: "EXC_BAD_ACCESS"", on the first line in this code:
-(Point3D *) unit {
NSLog(#"%#%#", "X: ", x);
double length = [self length];
return [[Point3D alloc] initWithX:x/length andY:y/length andZ:z/length];
}
called from here:
-(id) initWithStart:(Point3D *)start andDirection:(Point3D *)dir {
if ( self = [super init] ) {
NSLog(#"%#%#", #"Direction:", [dir toString]);
printf("Trying to find unit length of direction...\n");
NSLog(#"%#", [[dir unit] toString]);
self.start = start;
self.direction = [dir unit];
}
return self;
}
Console output is:
2011-12-09 17:20:14.021 RayTracerProject[16607:407] Direction:(0,0,20)
Trying to find unit length of direction...
The toString method of the Point3D looks like this:
-(NSString *) toString {
NSNumber *xstring = [NSNumber numberWithDouble:self.x];
NSNumber *ystring = [NSNumber numberWithDouble:self.y];
NSNumber *zstring = [NSNumber numberWithDouble:self.z];
NSString * str = #"(";
str = [str stringByAppendingString:[xstring stringValue]];
str = [str stringByAppendingString:#","];
str = [str stringByAppendingString:[ystring stringValue]];
str = [str stringByAppendingString:#","];
str = [str stringByAppendingString:[zstring stringValue]];
str = [str stringByAppendingString:#")"];
return str;
}
So, from what I can see, my (Point3D *) dir is alive and well when I check what the value is using my [dir toString] call. But when I try to call [dir unit], it seems I no longer have the variables I did in the object, hence the EXC_BAD_ACCESS error.
What am I doing wrong here? I think it's something to do with the way I'm managing (or not) my memory usage, but I don't know what it is.
The NSLog in your method 'unit' should be:
NSLog(#"%# %g", #"X:", x);
Moving the space is optional, but just makes it clearer. The key issues were that you were missing the # before the "X: ", and you need to use %g rather than %# because from your other code x is a double rather than an NSObject. Equivalent—but simpler and thus better—would also be:
NSLog(#"X: %g", x);
Apple's documentation provides the definitive guide to string format specifiers and that and the surrounding documentation explain the use of format specifiers like %# (to reference an Objective C object) and %g (to reference a 64-bit floating-point number) when constructing an NSString.
It seems this line here:
NSLog(#"%#%#", "X: ", x);
should look more like this:
NSLog(#"%#%lf", #"X: ", self.x);
Note a few changes here:
From your other code, it seems you have a property named 'x'. self.x references that property. (You may or may not also have an instance variable named 'x')
Also from your other code, it seems your property 'x' is a double. So the placeholder for a double is %lf, not %#. %# is for NSObjects (and descendants)
NSString literals start with #. So #"X: " is an NSString literal. What you had is a C string.
EDIT
Well, I'm slow as usual. :) Ignore what I said and accept Duncan's answer, which was faster and now cleaner, actually.