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;
Related
I'm trying to make an array of array of strings so that I can eventually pull out something like ArrayOfArrays[0][1] = "hi".
NSString *ArrayOne[] = {#"hello", #"hi"};
NSString *ArrayTwo[] = {#"goodbye", #"bye"};
NSArray *ArrayOfArrays[] = {#[*ArrayOne, *ArrayTwo]};
However when I try to do this, I get an error: Initializer element is not a compile-time constant.
I've read that this is because I'm creating an array with dynamic values, though it should be static. Not sure how to work around this.
Any advice on making an array of array of strings?
Use NSArray, or rather NSMutableArray if you want to modify it after creation:
NSMutableArray *arrayOne = [#[#"hello", #"hi"] mutableCopy];
NSMutableArray *arrayTwo = [#[#"goodbye", #"bye"] mutableCopy];
NSMutableArray *arrayOfArrays = [#[arrayOne, arrayTwo] mutableCopy];
There are other ways to initialise it, but this is the only way that allows you to use Objective-C literal syntax.
You cannot store plain ol' C arrays within an Objective-C collection class as your code attempts to do.
You wrote:
it should be static
if this is what you want then your use of C arrays is quite valid, you just got the syntax wrong. You can use:
NSString *arrayOfArrays[][2] =
{ {#"hello", #"hi"},
{#"goodbye", #"bye"},
};
Important: The 2 is the number of elements in the inner array, you do not change it when adding further pairs.
This will give you a compile-time static array.
If what you are making is a map from one word to another you might be better off with a dictionary, e.g.:
NSDictionary *wordMap =
#{ #"hello" : #"hi",
#"goodbye" : #"bye"
};
and accessing an element becomes:
wordMap[#"hello"];
Note: the dictionary "constant" here is actually executed code; the C array version can appear as a global or local initialiser, while the dictionary initialisation must be done in a method/function - but it can assign to a global.
HTH
NSArray *array = #[
#[[ #"hello", #"hi" ] mutableCopy],
#[[ #"goodbye", #"bye" ] mutableCopy],
];
NSLog(#"%# is short for %#", array[0][1], array[0][0]);
Output: hi is short for hello
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)
I am completely new to Objective-C and although i have some experience with java and C#, I just can't get this to work.
My code is:
- (IBAction)btnClickMe_Clicked:(id)sender {
Label_1.text = (#"some string" + _Label_2.text);
}
I am also curious as to why Label_1 does not need an underscore infront of it, like _Label_2 does?
To concatenate strings, you use
Label_1.text = [#"Some string" stringByAppendingString:_Label_2.text];
You can use %# to append your additionnals strings with stringWithFormat
Label_1.text = [NSString stringWithFormat: #"Some string %#", _Label_2.text];
More example : Apple - Formatting String Objects
NSString provides a vast variety of methods for string manipulations. Amongst them are several ways for conatination.
You should get familiar with the factory method stringWithFormat. It is one of the most powerful and especially good at a bit more complex requirements.
In your case:
Label_1.text = [NSString stringWithFormat:#"Some string%#", _Label_2.text);
or
Label_1.text = [NSString stringWithFormat:#"%#g%#", #"Some string", _Label_2.text);
The format string corresponds to the usual standard c printf format string plus the %# tag which is replaced by any objects description value. So you could have an NSNumber there or even an NSArray or so. However, the description of NSArray, NSDictionary, NSSet etc. may not really be useful for production but come quite handy for debugging. NSLog() uses the same format.
NSString *string = #"HELLO";
For some reason, XCode won't auto-complete methods like remove characters or append etc... If that's the case, how can I, say, remove certain characters from my string? Say I want to remove all the L's.
NSString doesn't respond to those methods. NSMutableString does, but you've declared an immutable string variable and assigned to it a string literal. Since an Objective-C #"string literal" is always immutable (an instance of NSString but not NSMutableString), there's no way those messages can be sent to the object you're using.
If you want a mutable string, try:
NSMutableString *mutableString = [[#"HELLO" mutableCopy] autorelease];
That's an immutable string literal.
Here is a great post explaining it in further details:
What's the difference between a string constant and a string literal?
As for your question on how would you change it and remove the Ls:
NSString *hello = #"HELLO";
NSString *subString = [hello stringByReplacingOccurrencesOfString:#"L" withString:#""];
NSLog(#"subString: %#", subString);
That outputs "HEO"
Either that or you can create an NSMutableString by creating a copy of the mutable string like Jonathan mentioned. In both examples, you're copying it into a non-literal string.
I want to compare the value of an NSString to the string "Wrong". Here is my code:
NSString *wrongTxt = [[NSString alloc] initWithFormat:#"Wrong"];
if( [statusString isEqualToString:wrongTxt] ){
doSomething;
}
Do I really have to create an NSString for "Wrong"?
Also, can I compare the value of a UILabel's text to a string without assigning the label value to a string?
Do I really have to create an NSString for "Wrong"?
No, why not just do:
if([statusString isEqualToString:#"Wrong"]){
//doSomething;
}
Using #"" simply creates a string literal, which is a valid NSString.
Also, can I compare the value of a UILabel.text to a string without assigning the label value to a string?
Yes, you can do something like:
UILabel *label = ...;
if([someString isEqualToString:label.text]) {
// Do stuff here
}
if ([statusString isEqualToString:#"Wrong"]) {
// do something
}
Brian, also worth throwing in here - the others are of course correct that you don't need to declare a string variable. However, next time you want to declare a string you don't need to do the following:
NSString *myString = [[NSString alloc] initWithFormat:#"SomeText"];
Although the above does work, it provides a retained NSString variable which you will then need to explicitly release after you've finished using it.
Next time you want a string variable you can use the "#" symbol in a much more convenient way:
NSString *myString = #"SomeText";
This will be autoreleased when you've finished with it so you'll avoid memory leaks too...
Hope that helps!
You can also use the NSString class methods which will also create an autoreleased instance and have more options like string formatting:
NSString *myString = [NSString stringWithString:#"abc"];
NSString *myString = [NSString stringWithFormat:#"abc %d efg", 42];