discuss "stringByReplacingOccurrencesOfString:withString:" - objective-c

I am thinking about some details regarding
stringByReplacingOccurrencesOfString:withString: in NSString class
According to document.
It returns a new string in which all occurrences of a target string in the receiver are replaced by another given string.
- (NSString *)stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement
Parameters
target
The string to replace.
replacement
The string with which to replace target.
my question is that if the replacement string is not found in the target string. If there would be some side effects to call this function.
Thanks
For example
NSString *myString = [NSString stringWithString:#"Hello my string"];
NSString *myReplacementString = [myString stringByReplacingOccurrencesOfString:#"NOTFOUND" withString#"Any side effect?"];
NSLog(#"my replacement string is %#", myReplacementString);

That function has no side effects, and if the search string isn't found in the receiver, the receiver will be returned unaltered.

There are no side effects: if the substring to be replaced doesn't occur in the original string, you end up with an NSString identical to the one you started with.
Indeed, (this is an implementation detail and you shouldn't depend on it, but) you don't even end up with a copy of the original string, but a pointer to the same exact string.

myString, target and replacement aren't modified whether a match if found or not:
The method doesn't change the string myString in place but creates a new string that will contain the result.
target and replacement are parameters of the method and their values don't get changed.
myString, target and replacement are immutable strings.
There are no errors or exceptions if no match is found.

Related

Do instance references really work in Swift?

I wrote the Objective-C code first
NSMutableString *aStrValue = [NSMutableString stringWithString:#"Hello"];
NSMutableDictionary *aMutDict = [NSMutableDictionary dictionary];
[aMutDict setObject:aStrValue forKey:#"name"];
NSLog(#"Before %#",aMutDict);
[aStrValue appendString:#" World"];
NSLog(#"After %#",aMutDict);
I got the output as follows
2015-09-17 14:27:21.052 ShareIt[4946:129853] Before {
name = Hello;
}
2015-09-17 14:27:21.057 ShareIt[4946:129853] After {
name = "Hello World";
}
Means when I append a string to a Mutable string which is actually referred into a MutableDictionary, the change is getting reflected in Dictionary too..
But then I tried something same in Swift
var stringValue:String?
stringValue = "Hello"
var dict:Dictionary = ["name":stringValue!]
println(dict)
stringValue! += " World"
stringValue!.extend(" !!!!")
println(dict)
I seen the output in playground like this
My Questions are
Why the value that changed is not reflecting in a data structure like
Dictionary.
Does in Swift adding any key value really keeps the value or its
reference, if it's keeping the reference like objective-C then here what is my mistake?
Reference type
The different behaviours depends on the fact that in the Objective-C code you use NSMutableString that is a class.
This means that aMutDict and aStrValue are references to the same object of type NSMutableString. So the changes you apply using aStrValue are visibile by aMutDict.
Value type
On the other hand in Swift you are using the String struct. This is a value type. This means that when you copy the value from one variable to another, the change you do using the first variable are not visible to the second one.
The following example clearly describes the value type behaviour:
var word0 = "Hello"
var word1 = word0
word0 += " world" // this will NOT impact word1
word0 // "Hello world"
word1 // "Hello"
Hope this helps.
Strings in Swift (copy by value) are completely different than string in Objective C (copy by reference).
From Apple' Swift documentation:
Strings Are Value Types
Swift’s String type is a value type. If you create a new String value,
that String value is copied when it is passed to a function or method,
or when it is assigned to a constant or variable. In each case, a new
copy of the existing String value is created, and the new copy is
passed or assigned, not the original version. Value types are
described in Structures and Enumerations Are Value Types.
Swift’s copy-by-default String behavior ensures that when a function
or method passes you a String value, it is clear that you own that
exact String value, regardless of where it came from. You can be
confident that the string you are passed will not be modified unless
you modify it yourself.
Behind the scenes, Swift’s compiler optimizes string usage so that
actual copying takes place only when absolutely necessary. This means
you always get great performance when working with strings as value
types.
In swift, String is a Struct.
Structs are not reference types in Swift, thus it's copied when you setting it to a dictionary.

Variadic function: Checking number of arguments

I have a variadic function that runs some code on the first argument and then runs NSString initWithFormat:arguments: afterwards, if arguments have been passed in.
+ (NSString *)updatedString:(NSString *)mainString, ...
{
// Some code run on mainString here, outputs finalString
// add format arguments to the final string
va_list args;
va_start(args, mainString);
NSString *formattedString = [[NSString alloc] initWithFormat:finalString arguments:args];
va_end(args);
return formattedString;
}
EDIT: The idea is that the code run on mainString uses a regular expression to find and replace variables within the text. So say the output (finalString) equals "Hello World %# blah blah %#", then it would use the arguments to replace the %# symbols. The issue is that I don't know if the resolved string includes %# symbols or not until the string is resolved.
So, I need to check to see if there are actually any extra arguments, and if there aren't, I don't want to run the initiWithFormat part.
Is there any way to check if args exists first?
No. In C/Objective-C a called variadic function has absolutely no idea about the number and types of arguments passed by the caller. It must figure it out somehow based on other information (e.g. for format arguments, that they match the format specifiers in the format string; or for objects to initialize a Cocoa collection, that the list is terminated with nil) and trust that the caller correctly followed the convention.

Getting warning in iOS : Format String is not a string literal (potentially insecure)

Format String is not a String literal (potentially insecure)
My code is:
[tweetViewController setInitialText:[NSString stringWithFormat:self.url]];
You're getting this warning as you're using a variable when only a string literal (usually a format string) is allowed as an argument to this method.
If you don't want to use a format string don't use that method and use the absoluteString method of NSURL instead:
[tweetViewController setInitialText:[self.url absoluteString]];
Or if you do want to use the format method use a format string:
[tweetViewController setInitialText:[NSString stringWithFormat:#"URL: %#", [self.url absoluteString]];
Just using self.url as the argument for the %# will use the description method on NSURL which might return different results if Apple ever change the result of this method.

stringByAppendingString on same object

i want to ask a question on NSString, the question is if
NSString str = #"Hello";
str = [str stringByAppendingString:#"World"];
if we NSLog the str we would get an output - HelloWorld!
So my question is if str is NSString class variable an its an static one (which can not be changed once it is defined) then how can we able to change it, (Note that I have used same NSString object str).
You haven't declared the string as static, but NSString is immutable. You are creating a new string and replacing str. Many Obj-C classes have a mutable type, so if you intended to modify your string as in this example, you might want something more like:
NSMutableString *str = [NSMutableString stringWithString:#"Hello"];
[str appendString:#" World"];
Note, #"Hello" is a NSString, so attempting to initialize a NSMutableString using that syntax would result in an error.
Actually, static variables can be changed once they are defined. Static variables are simply maintained throughout all function/instance calls. You can find a more detailed explanation here static variables in Objective-C - what do they do?. So the result would be the same. If the variable were defined as const (which cannot be changed) then you would get a compiler error. Generally speaking, the best way to figure these types of things out is to just try them on your own. If it's just a matter of writing a couple lines of code, or changing a keyword, what does it hurt?

Issue comparing NSString literal and constant

I have an NSString, firstWord, that has the value of "First". It was defined like this:
NSString *firstWord = [[NSString alloc] init];
firstWord = [textView text];
However, when I try to check its value, like so:
if (firstWord == #"First"){}
The comparison does not return true, or YES I guess in Objective-C. I know that it does have the same value as a string. So, from this I am pretty sure that the issue is that I am comparing pointers, which are not the same here, even though the strings themselves do have the same value. So how can I directly compare the strings themselves? Or maybe more efficiently, make sure the two string objects do have the same pointer, so I don't have to the do the relatively costly string comparison?
So how can I directly compare the strings themselves?
String comparison in Cocoa is done with isEqualToString:.
Or maybe more efficiently, make sure the two string objects do have the same pointer,
This isn't possible. One is a string literal, stored in the DATA section of your app's binary; the other is on your app's heap. In certain circumstances (creating a string using initWithString:#"literal string") you'll end up with the same address, but you shouldn't rely on that.
As an aside, you don't need to -- in fact, shouldn't, because you're creating a leak -- allocate a string before assigning the text view's text to the pointer.
There are actually two errors.
Your actual question:
if (firstWord == #"First"){}
needs to be
if ([firstword compare:#"first"]==NSOrderedSame) ...
Only works if firstword isn't nil.
In addition:
NSString *firstWord = [[NSString alloc] init];
firstWord = [textView text];
This looses memory, since you are not "copying" the textview into the string (which wouldn't be possible anyway since it's not mutable), you are simply assigning another string object to the pointer. So the empty string that you've allocated is lost.
Just do
NSString *firstWord;
firstWord = [textView text];
EDIT: making sure the strings have the same pointers would be even more costly, since you would have to keep a table of string objects and lookup every string there so you can either replace your pointer with the "unique" one or add the new string to the table...
Start to learn Objective-C from Swift. Here is what I do to give different layout code per deviceModel type.
#property (nonatomic, strong) NSString *deviceModel;
NSString *iPhoneSE = #"iPhone8,4";
if ([self.deviceModel isEqualToString:iPhoneSE]) { // iPhone SE
// layout code
}