I want to compare the following simple assignments:
...
#property(nonatomic,retain) UITextField *textField;
...
self.textField.text = #"some string";
self.textField.text = [NSString stringWithString:#"some string"];
self.textField.text = [NSString stringWithFormat:#"some string"];
Where textField is an UITextField and the text property a NSString. Of course all of them work. I know the difference of the last two when using parameters. But lets say we are only interested in this usage.
QUESTIONS:
For doing this kind of assignment, why shouldn't I always use the first one?
Comparing the last two, is there any difference for the compile- and/or runtime of these two? And why should I use stringWithString: at all if not?
Always try to do what feels natural. If you're assigning a constant string then do that, i.e. the first option. #"..." strings are very efficient constants that do not need to be memory managed, so use them if it makes sense.
NSLog(#"%p", #"XX");
NSLog(#"%p", #"XX");
NSLog(#"%p", #"XX");
Results in:
0xa2424
0xa2424
0xa2424
i.e. They are all the same object in memory.
NSLog(#"%p", [NSString stringWithString:#"XX"]);
NSLog(#"%p", #"XX");
NSLog(#"%p", [NSString stringWithString:#"XX"]);
Also results in:
0xa2424
0xa2424
0xa2424
As you can see from this there is no difference between the two objects, thus using -stringWithString: is just an extra message to send. Having said that, the overhead is usually not big enough to make a difference, so it shouldn't be a big deal either way. Personally I'd go with method one as there is no benefit of using method two, it's just extra code.
However,
NSLog(#"%p", [NSString stringWithFormat:#"XX"]);
NSLog(#"%p", [NSString stringWithFormat:#"XX"]);
NSLog(#"%p", [NSString stringWithFormat:#"XX"]);
Results in:
0x7f86730
0xf8479b0
0x8a4cdb0
As you can see, a new string is created each time as the sting you provide is just a format string that is used to process the following substitution variables, as you have none avoid stringWithFormat: unless you need it.
(Obviously all addresses are examples...)
For doing this kind of assignment, why shouldn't I always use the first one?
For that kind of assignment you would always use the first one, and never the last two.
why should I use stringWithString: at all if not?
Your intuition is correct. In most cases -stringWithString: is of dubious value. It's primarily meant for use with NSMutableString, which is a subclass of NSString.
For example:
NSMutableString* myString = [NSMutableString stringWithString:#"Foo"];
[myString appendString:#"Bar"];
You can also use it if you want to convert an NSMutableString to NSString, or otherwise ensure that you're dealing with an NSString instance. For example:
- (void):setMyString:(NSString*)newString
{
[_myString release];
_myString = [[NSString stringWithString:newString] retain];
}
That's one way to ensure that the _myString ivar is pointing to an NSString instance and not an NSMutableString instance. And the newString instance is only copied if necessary.
However, most developers would just use _myString = [newString copy]; in that case.
For doing this kind of assignment, why shouldn't I always use the first one?
You should always use the first one in the situation you describe. The second and third cases potentially copy the constant string, but the text property of UITextField is specified as copying the provided string anyway. There's no sense in making a copy of a constant string just so UITextField's -setText: can copy that copy.
Comparing the last two, is there any difference for the compile-
and/or runtime of these two? And why should I use stringWithString: at
all if not?
My understanding is that -stringWithFormat: will always create a new string, while -stringWithString: might not (probably doesn't) for a constant string. hypercrypt's results above are pretty telling in this respect; if you wanted to explore that more, you might try the same test with a mutable string.
I don't think it matters if you use the first or the second for strings.
I usually use the second one however.
In the case of the second and third, if you have another variable you would like to include in your string then you use the stringWithFormat one. Otherwise, use stringWithString.
int number = 5;
NSString *str = [NSString stringWithFormat:#"Number is: %i", number];
// Str is "Number is: 5"
Situation where you would use stringWithString:
NSString *myName = [NSString stringWithString:#"FN LN"];
// myName is "FN LN"
You would use the latter when you have no other variables to include in the string.
Also, this question has been answered countless times elsewhere.
Related
I have NSData objects storing data (non character / non-ascii). I'm trying to put it into an array without it being interpreted as characters or ascii. I know this question has been asked a few times before, but none of the solutions posted have worked for me in this situation. I'm trying to avoid using property lists, which is what most answers suggested. I already tried converting the NSData to an NSString, then storing the string in the array, but of course it is interpreted as characters after putting it in the string, regardless of the encoding I've used. For example, one of the NSData's contains the value 2c, and when I put it into a string it is interpreted as ,. Does anyone know how I can store the raw data, in its original state, in an NSArray? Maybe by storing the data in user defaults, then somehow storing the defaults in an array? I'm at a loss.
Here is some possibly relevant code:
NSData *receivedData = [bleDevice readData];
NSString *receivedDataString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
[dataArray insertObject:receivedDataString atIndex:0];
When I call:
[dataArray insertObject:receivedDataString atIndex:0];
It will store something like 2c ad a ,.
But, when I try and insert the raw data, like:
[dataArray insertObject:receivedData atIndex:0];
It will simply not store anything. There are no warnings, no errors. I'll NSLog the array and it is null.
[dataArray insertObject:receivedData atIndex:0]; most certainly will insert "receivedData" into "dataArray" (so long as both exist). "receivedData" can be any sort of NSObject -- need not be a string. If the array is "null" when you log it then the array itself never got created.
(It's important to remember that if an object pointer is nil then method calls on that pointer do not fail but rather silently return zero/nil, so "returns nil" strongly suggests the object never was created.)
NSString *s1 = #"string1";
// NSString *s2 = [[NSString alloc]stringWithFormat:#"string2"];
NSString *s2 = [[NSString alloc] initWithFormat:#"string2"];
I know this is pretty basic concept but I am not 100% clear with this.
First one doesn't allocate a memory address and send one allocates a memory address...
Also second one increases the reference count of the variable but not the first one...
Even though I understand the concept but don't know the situation when I should use the first one or the second one..
When should I use the first one ? also when should use for the second one?
Thanks in advance..
Carl is is right, but there is some subtlety to keep in mind. Consider this:
NSString *s1 = #"string1";
NSString *s2 = [[NSString alloc] initWithString:#"string1"];
NSString *s3 = [NSString stringWithString:#"string1"];
NSString *s4 = [NSString stringWithFormat:#"string1"];
NSLog(#"ptrs %p %p %p %p", s1, s2, s3, s4);
The output is:
ptrs 0x1000010c0 0x1000010c0 0x1000010c0 0x100108da0
That's right; s1, s2, and s3 are all pointing to the same string instance while s4 ends up being a new instance. This is because NSString detects when a string is being initialized from a constant string and, when perfectly valid to do so, returns the constant string instead of bothering to allocate a new chunk of memory to hold the same thing. The stringWithFormat: case is not so optimized likely because all the potential format string processing makes such an optimization both moot and, likely, unnatural to the implementation.
In other words, do not assume that two strings are the same or different merely by code inspection or by pointer comparison. Use isEqual: or, better yet, isEqualToString:.
You should never use the second one - +stringWithFormat: is a class method. Including your first example, you have basically 3 choices:
NSString *s1 = #"string1";
NSString *s2 = [[NSString alloc] initWithString:#"string2"];
NSString *s3 = [NSString stringWithFormat:#"string3"];
s1 in this case is a pointer to a constant string. s2 and s3 both point to new strings that you've created, but s2 has been retained for you, and s3 has been autoreleased. If you just need a temporary object, the autoreleased object s3 or the constant object s1 are good choices. If you need to keep the string around, you should use s2 (actually now that I think about it s1 will work in this case too - it's not really idiomatic, though).
You can make s1 or s3 equivalent to s2 by sending them a retain message. You could also turn s2 into an equivalent object by sending it an autorelease message.
A string literal like #"string1" (or the #"string2" on the second line) is, as you've noted, not an allocation. The string is actually stored as a constant in your executable's data section, just like any constant you've declared. Strings created this way don't really have a reference count; you can send -retain and -release messages to them and nothing will happen.
Your second line is ill-formed; +stringWithFormat: is actually a class method, and it doesn't really make sense to use +stringWithFormat: without having any format parameters. In general, just use the string literal form if you don't have a specific reason to use another form. There's no need to allocate a new object if there's already the constant one floating around your application.
Don't use any ...WithFormat: method if you're just going to pass it a constant string. No need to parse the format string for the trivial case.
Well, I've been learning Objective-C for a while now, and I don't get why creating an NSArray would be beneficial for you. It's just a collection of some stuff right? Why can't you just use them without making an NSArray. Or can you use the objects in it in the implementation of every one of your methods (even if it's a local ivar).
So, any help would be appreciated. Thanks guys!
You could make instance variables for every “item” you need:
NSString *str1;
NSString *str2;
NSString *str3;
…but that’s hard to work with, an array is simply more convenient. What would you do if you wanted to print all these strings?
NSLog(#"%#", str1);
NSLog(#"%#", str2);
NSLog(#"%#", str3);
Wouldn’t it be easier to loop over an array?
NSArray *strings = [NSArray arrayWithObjects:#"one", #"two", #"three", nil];
for (NSString *str in strings)
NSLog(#"%#", str);
How about if you have ten, twenty strings? And what if you don’t know how many strings you will need? What if you want to pass all these items to somebody else? Are you going to pass them one by one?
- (void) doSomethingWithString1: (NSString*) str1 andString2: (NSString*) str2…;
Or would you rather pass an array?
- (void) doSomethingWithMyStrings: (NSArray*) strings;
Very often, the NSArray you see is a front for an NSMutableArray working behind the scenes.
Billing it in the interface as an NSArray is just a way to ensure that it cannot be manipulated from without, which could have moderately disastrous consequences. (Thus, when asking for an NSArray from an object, you'll usually get a copy of the NSMutableArray used internally.)
Understanding why creating an NSMutableArray is beneficial is left as an exercise to the reader.
Different contexts.
When you use an NSString it's generally used for a single value. It's quite simple.
NSArray
If you want to loop through many keys/values, use an NSArray. It is not possible to loop through NSStrings because they don't have the order structer that NSArray's do.
Why use NSString
It would be silly to call myArray[0] and foo[0] every time you saved just a value. NSString is simpler than NSArray, since you can easily set a value on a GUI element (without having to call myArray[0])
Why use NSArray
It is more flexible than NSString because NSArray can hold multiple values, which may be needed in certain situations.
Weighing pros and cons
Pros (NSArray):
Can make your code cleaner (call myArray[1] and myArray[0] instead of mystring and foo)
Simplified memory management (easier to release one NSArray than many NSStrings)
Cons (NSArray):
Can make your code very complicated (was that on myArray[0] or 1?)
Might be difficult to know what type of data is in the array (if it's an NSString, it's always going to be a string, an array can hold many different types of data). You wouldn't use 50 tissues to dry yourself off from the shower but use a towel.
So it boils down to one thing. What context are you using?
This code...
NSString * s = [[NSString alloc] initWithString:#"Hello, World"];
s = s.lowercaseString;
NSLog(#"%#", s);
...allows the use of dot notation but is strongly typed.
This code...
id s = [[NSString alloc] initWithString:#"Hello, World"];
s = [s lowercaseString];
NSLog(#"%#", s);
... is weakly typed and requires use of square brackets.
Other than that, is there any advantage of using one over the other?
If you're creating an NSString, then you might as well declare it as an NSString, and let the compiler help you.
The point of using id is to prevent strong coupling, and to use objects whose types are not known until a later time. e.g IBAction methods include the sender as a parameter as an id, because the exact type of the object isn't known.
Edited to add:
You may be new to the language, so I'll mention a couple of things
Firstly, where you have #"Hello, World", you already have an NSString, just one that is static. So you don't need to go through initWithString to create it. Just write:
NSString *s = #"Hello, World";
And, because you didn't alloc it, you don't have to worry about releasing it.
Secondly s.lowerCaseString. As Stephen has already answered, this is considered to be bad style. When you change a string to lower case, you aren't getting a property of the the string, you are causing an operation to be done on the string, in which case, you really should use bracket syntax.
Yes. The compiler warns you if you try to put a NSString into a method that expects a NSNumber.
It's more likely that the compiler finds your mistakes.
Arguably the former code is incorrect. You should only really use the dot notation to get/set properties, and lowercaseString is a method.
Otherwise, as you suggest, the only real difference is type safety. If you had a typo, say you put [a loercaseString], the compiler wouldn't shout at you.
There are certainly cases where you'd use id but your example is not one of them
In the Pragmatic Core Data book, I came across this code snippet for an NSString setter:
- (void)setMyString:(NSString*)string;
{
#synchronized(self) {
if ([string isEqualToString:myString]) return;
[myString release];
myString = [string retain];
}
}
Is there any reason to use [string isEqualToString:myString] instead of string == myString here? Does it not mean that if the two strings have the same content, the result will be different than if they are actually the same object? Does this matter?
Thanks.
Notice that the variables you're comparing are pointers to NSStrings. Pointer comparison just checks if the pointers are referring to the same address. It doesn't know anything about the content at the end. Two string objects in two different places can have the same content. Thus you need isEqualToString:. In this case, I'm not sure either that it's a terribly important distinction to make though. It would make more sense to me if it were special-casing sending out change notifications based on whether the new string would actually be a change.
Incidentally, in an NSString setter, you almost always want copy rather than retain. I don't know the exact use case in this book, but if you just retain the string and it happens to be mutable, it can change behind your back and cause weird results. And if the string isn't mutable, copy is just an alias for retain.