NSString concatenate on creation - ios

I'm trying to concatenate 2 strings assigning the result to a new string.
Normally I would do this way:
NSString * s = [NSString stringWithFormat: #"%#%#", str1, str2];
Now I wish s to be static
static NSString * s = [NSString stringWithFormat: #"%#%#", str1, str2];
but compiler kick me with "Initializer element is not a compile-time..."
Is there any way to do this? I Googled a bit with no results and also I have not found answers on StackOverflow asking the question.
And what about using a short form like (in PHP)
$s = $str1.$str2;
Any help will be appreciated.
EDIT: What i want to achieve is to have a config file like this (in PHP code)
define ("BASE_URL", "mysite.com/");
define ("SERVICE_URL1", BASE_URL."myservice1.php?param1=value1");
define ("SERVICE_URL2", BASE_URL."myservice2.php?param2=value2");
I prefer to have all configurations strings in 1 file and i found usefull static strings in objective c. Just want to put 2 usefull thing together :)
EDIT2: There's no metter if i obtain this with defines, but the NSString way is preferred and i use static just beacause const make me some compilation problems i haven't solved yet

Use this code for creating static s:
static NSString * s = nil;
if (!s)
s = [NSString stringWithFormat: #"%#%#", str1, str2];
Also for concatenating two string you case use such code: NSString *s = [str1 stringByAppendingString: str2];
UPDATED:
You can concat static string by putting them one by one.
Example:
#define STR1 #"First part" #" Second part"
#define STR2 #"Third part " STR1
NSLog(#"%#", STR2);
This cole will print Third part First part Second part

I think below lines may help:
NSString *str1 = #"String1";
NSString *str2 = #"String2";
NSString *combinedStr = [str1 stringByAppendingString:str2];

If you can use a define, it is pretty simple:
#define A #"a"
#define B #"b"
…
static NSString *ab = A B; // or: #"A" #"B"
You can always concatenate string literals with a single space.
But something very important has to happen to use defines. What's wrong with computing it non-static or compute it once?
BTW: You should use dispatch_once() and not if. For the reasons you can search "dispatch_once" on SO.

If you don't mind compiling Objective-C++ code, you could simply change the extension from .m to .mm, by default XCode compiles according to file type, and this is valid in Objective-C++

Solved this way:
#define kBaseURL #"mysite.com/"
static NSString *kServiceUrl1 = kBaseURL #"myservice1.php?param1=value1";
static NSString *kServiceUrl2 = kBaseURL #"myservice2.php?param2=value2";
thanks all.
now the question is
wich one i have to accept as right answer? I mean, mine is the solution, but I would never have got there without your help guys

Related

What exactly does #"" do behind the scenes?

When giving a NSString a value using = #"", what exactly is that shorthand for? I noticed that, all other things being equal in an application, global NSStrings made with #"" don't need retain to be used outside of the methods that gave them that value, whereas NSStrings given a value in any other way do.
I have found this question utterly unsearchable, so I thank you for your time.
A related answer: Difference between NSString literals
In short, when writing #"" in the global scope of your program, it does the same as writing "": The literal string you specified will be embedded in your binary at compile time, and can thus not be deallocated during runtime. This is why you don't need release or retain.
The # just tells the compiler to construct a NSString object from the C string literal. Mind you that this construction is very cheap and probably heavily optimized. You can follow the link and see this example:
NSString *str = #"My String";
NSLog(#"%# (%p)", str, str);
NSString *str2 = [[NSString alloc] initWithString:#"My String"];
NSLog(#"%# (%p)", str2, str2);
Producing this output:
2011-11-07 07:11:26.172 Craplet[5433:707] My String (0x100002268)
2011-11-07 07:11:26.174 Craplet[5433:707] My String (0x100002268)
Note the same memory addresses
EDIT:
I made some test myself, see this code:
static NSString *str0 = #"My String";
int main(int argc, const char * argv[]) {
NSLog(#"%# (%p)", str0, str0);
NSString *str = #"My String";
NSLog(#"%# (%p)", str, str);
NSString *str2 = [[NSString alloc] initWithString:#"My String"];
NSLog(#"%# (%p)", str2, str2);
return 0;
}
Will produce this output:
2015-12-13 21:20:00.771 Test[6064:1195176] My String (0x100001030)
2015-12-13 21:20:00.772 Test[6064:1195176] My String (0x100001030)
2015-12-13 21:20:00.772 Test[6064:1195176] My String (0x100001030)
Also, when using the debugger you can see that the actual object being created when using literals are in fact __NSCFConstantString objects.
I think the related concept to that is called Class Clusters
Objective-C is a super-set of C, which means you should be able to write C code in there and have it work. As a result, the compiler needs a way to distinguish between a C string (an old-school series of bytes) and an Objective-C NSString (Unicode support, etc). This is done using the # symbol, such as #"Hello".
#"Hello" can't be released because it's a literal string – it's been written into the program when it was built, versus being assigned at run-time.
String allocated using the literal syntax i.e # reside as c strings in the data segment of the executable. They are allocated only once at the time of program launch and are never released until the program quits.
As an exercise you can try this:
NSString *str1 = #"Hello";
NSString *str2 = #"Hello";
NSLog("Memory Address of str1 : %p", str1);
NSLog("Memory Address of str2 : %p", str2);
both the log statements will print the same address which means literals are constant strings with lifetimes same as that of the program.

Replace multiple placeholders in string

What I am looking to do is use placeholders in a string and replace the placeholders with data specific to the user. I can setup the placeholders to be anything so basically I am looking to do the following:
Setup placeholders in a string (up to 4 placeholders)
Replace those placeholders with strings I specify
Here is what I have. I currently have a url that has a set of placeholders like so. http://example.com/resource?placeholder1=placeholder2 or http://placeholder1:placeholder2#example.com/something?placeholder3
How do I properly label the placeholders and replace them?
Thank you in advance for any help.
You can use the stringByReplacingOccurrencesOfString method as below
NSString *strUrl = #"http://example.com/resource?placeholder1=placeholder2";
strUrl = [strUrl stringByReplacingOccurrencesOfString:#"placeholder1" withString:#"value1"];
strUrl = [strUrl stringByReplacingOccurrencesOfString:#"placeholder2" withString:#"key1"];
Quote : "I want to replace placeholder1 with an NSString I have already created called value1 and placeholder2 with an NSString called key1."
NSString *mainUrl = "http://example.com/resource";
NSString *string1 = "value1";
NSString *string2 = "value2";
Now change your URL:
NSString *newURL = [NSString NSStringWithFormat:#"%#?%#=%#",mainUrl,string1,string2];
This will generate newURL : http://example.com/resource?value1=value2
This might help you.
#define placeHolder1 #"<>p1p1p1<>"
#define placeHolder2 #"<>p2p2p2<>"
And place this in a function of yours where you want to replace strings
NSString * string = [NSString stringWithFormat:#"http://example.com/resource?%#=%#",placeHolder1,placeHolder2];
NSLog(#"string %#",string);
NSString * replacerForP1 = #"123";
NSString * replacerForP2 = #"741";
string = [string stringByReplacingOccurrencesOfString:placeHolder1
withString:replacerForP1];
string = [string stringByReplacingOccurrencesOfString:placeHolder2
withString:replacerForP2];
NSLog(#"string %#",string);
It would be best to keep your placeholder strings in the constants defined somewhere. And ofcourse the replacement of those placeholders will be dynamic as you said so cannot make them constants.
Tell me if this helps or if you require further assistance in the matter.

Multiple NSString declaration and initialization

I am new to the ios development.I was checking the NSString and where I had found out that it was excessed using the multiple ways which is as under
1) NSString *theString = #"hello";
2) NSString *string1 = [NSString stringWithString:#"This is"];
3) NSString *string =[[NSString alloc]init];
string =#"Hello";
Now I am confused with above three and would like to know the differences between then?
Yes all three are ways to create string...I try to explain you one by one.
One think you must remember that in Objective-c string is represented by #"".
whatever comes double quotes is string eg #"Hello" is string. #"" is basically literal to create NSString.
NSString *theString = #"hello";
EDIT:
In this statement we are creating an NSString constant. Objective-C string constant is created at compile time and exists throughout your program’s execution.
2. NSString *string1 = [NSString stringWithString:#"This is"];
In this statement again, we are creating an autorelease object of NSString, but here a slide diff. than the first case. here we are using another NSString object to create a new autorelease object of NSString. So generally we use stringWithString method when we already have NSString Object and we want another NSString object with similar content.
3. NSString *string =[[NSString alloc]init];
string =#"Hello";
Here, In first statement you are creating a NSString Object that you owned and it is your responsibility to release it (In non-ARC code), once you done with this object.
Second statement is similar to case 1, by string literal you are creating string Object.
but when you put these two statements together it causes you memory-leak(In non-ARC code), because in statement one you are allocating & initiating memory for new string object and right after in second statement you are again assigning a new string object to the same string reference.
1) and 2) is the same. there is no difference. [NSString stringWithString: str] just does nothing and returns str if str is immutable already. via this link
3) you create empty NSString pointer. after you assign this string with ' = ' is same way with 1) and it alloc memory to your NSString. You can watch this NSString pointer in 3) and you see pointer was changed:
NSString *string =[[NSString alloc]init];
printf("\n string_1-> = %p\n", string );
string = #"Hello";
printf("\n string_2-> = %p\n", string );
OK, first off, the syntax #"blah" is an Objective-C literal to create an NSString. Essentially it is an NSString.
Also, there are two parts to each statement. The declaration of the string variable NSString *string and the instantiation string = <something>.
So...
This is how you would do it most of the time. You declare the variable and set it using the objective-c literal.
This adds a bit of redundancy and you wouldn't really use it like this. You'd maybe use it with a substring or something from another string. As it is you are essentially creating two objects here. The first is [NSString stringWithString:<>]; and the second is #"This is".
This is a bit odd. You are first creating an empty string #"" and then you are replacing this string with #"Hello". Don't do this. There is no point. You may as well just do number 1 as the result is the same.
All three will work but in these exact cases you'd be better off using the first.

Adding a percentage % sign to an NSString [duplicate]

I want to have a percentage sign in my string after a digit. Something like this: 75%.
How can I have this done? I tried:
[NSString stringWithFormat:#"%d\%", someDigit];
But it didn't work for me.
The code for percent sign in NSString format is %%. This is also true for NSLog() and printf() formats.
The escape code for a percent sign is "%%", so your code would look like this
[NSString stringWithFormat:#"%d%%", someDigit];
Also, all the other format specifiers can be found at Conceptual Strings Articles
If that helps in some cases, it is possible to use the unicode character:
NSLog(#"Test percentage \uFF05");
The accepted answer doesn't work for UILocalNotification. For some reason, %%%% (4 percent signs) or the unicode character '\uFF05' only work for this.
So to recap, when formatting your string you may use %%. However, if your string is part of a UILocalNotification, use %%%% or \uFF05.
seems if %% followed with a %#, the NSString will go to some strange codes
try this and this worked for me
NSString *str = [NSString stringWithFormat:#"%#%#%#", #"%%",
[textfield text], #"%%"];
uese following code.
NSString *searchText = #"Bhupi"
NSString *formatedSearchText = [NSString stringWithFormat:#"%%%#%%",searchText];
will output: %Bhupi%
iOS 9.2.1, Xcode 7.2.1, ARC enabled
You can always append the '%' by itself without any other format specifiers in the string you are appending, like so...
int test = 10;
NSString *stringTest = [NSString stringWithFormat:#"%d", test];
stringTest = [stringTest stringByAppendingString:#"%"];
NSLog(#"%#", stringTest);
For iOS7.0+
To expand the answer to other characters that might cause you conflict you may choose to use:
- (NSString *)stringByAddingPercentEncodingWithAllowedCharacters:(NSCharacterSet *)allowedCharacters
Written out step by step it looks like this:
int test = 10;
NSString *stringTest = [NSString stringWithFormat:#"%d", test];
stringTest = [[stringTest stringByAppendingString:#"%"]
stringByAddingPercentEncodingWithAllowedCharacters:
[NSCharacterSet alphanumericCharacterSet]];
stringTest = [stringTest stringByRemovingPercentEncoding];
NSLog(#"percent value of test: %#", stringTest);
Or short hand:
NSLog(#"percent value of test: %#", [[[[NSString stringWithFormat:#"%d", test]
stringByAppendingString:#"%"] stringByAddingPercentEncodingWithAllowedCharacters:
[NSCharacterSet alphanumericCharacterSet]] stringByRemovingPercentEncoding]);
Thanks to all the original contributors. Hope this helps. Cheers!

Objective-C: "format string is not a string literal (potentially insecure)" warning with macro

I'm using a macro to simplify returning localised strings, like so:
#define GetLocalStr(key, ...) \
[NSString stringWithFormat:[[NSBundle mainBundle] localizedStringForKey:key value:#"" table:nil], ##__VA_ARGS__]
Basically, if you have an entry in a localisation Strings file like "name" = "My name is %#";, calling
GetLocalStr( #"name", #"Foo" );
will return the NSString #"My name is Foo"
When I run it however, like:
NSString * str = GetLocalStr( #"name", #"Foo" );
I get the "format string is not a string literal" warning. Even following the advice of the other answers on SO about this warning and replacing it with:
NSString * str = [NSString stringWithFormat:#"%#", GetLocalStr( #"name", #"Foo" )];
I still get the warning, and besides, it kind of defeats the point of the macro making life easier.
How can I get rid of the warning short of wrapping all the GetLocalStr calls in #pragma suppressors?
Edit 27/08
After running through CRD's answer and doing some more tests, it seems like I made a bad assumption on the error. To clarify:
Localisation Strings file:
"TestNoArgs" = "Hello world";
"TestArgs" = "Hello world %#";
Code:
NSString * str1 = GetLocalStr( #"TestNoArgs" ); // gives warning
NSString * str2 = GetLocalStr( #"TestArgs", #"Foo" ); // doesn't give warning
The majority of my translations take no arguments, and those were the ones giving the warning, but I didn't make the connection until I read through CRD's answer.
I changed my single macro to two, like so:
#define GetLocalStrNoArgs(key) \
[[NSBundle mainBundle] localizedStringForKey:key value:#"" table:nil]
#define GetLocalStrArgs(key, ...) \
[NSString stringWithFormat:[[NSBundle mainBundle] localizedStringForKey:key value:#"" table:nil], ##__VA_ARGS__]
And if I call each one separately, there's no warnings.
I'd like GetLocalStr to expand to either GetLocalStrNoArgs or GetLocalStrArgs depending on if any arguments were passed or not, but so far I've been having no luck (macros are not my strong suit :D).
I'm using sizeof(#__VA_ARGS__) to determine if there's any arguments passed - it stringifys the arguments, and if the size is 1, it's empty (i.e. `\0'). Perhaps it's not the most ideal method, but it seems to work.
If I rewrite my GetLocalStr macro to:
#define GetLocalStr(key,...) (sizeof(#__VA_ARGS__) == 1) ? GetLocalStrNoArgs(key) : GetLocalStrArgs(key,##__VA_ARGS__)
I can use it, but I still get warnings everywhere it's used and there's no arguments passed, while something like
#define GetLocalStr( key,...) \
#if ( sizeof(#__VA_ARGS__) == 1 ) \
GetLocalStrNoArgs(key) \
#else \
GetLocalStrArgs(key,##__VA_ARGS__)
won't compile. How can I get my GetLocalStr macro to expand properly?
The Clang & GCC compilers check that format strings and the supplied arguments conform, they cannot do this if the format string is not a literal - hence the error message you see as you are obtaining the format string from the bundle.
To address this issue there is an attribute, format_arg(n) (docs), to mark functions which take a format string; alter it in some way without changing the actual format specifiers, e.g translate it; and then return it. Cocoa provides the convenient macro NS_FORMAT_ARG(n) for this attribute.
To fix your problem you need to do two things:
Wrap up the call to NSBundle in a function with this attribute specified; and
Change your "key" to include the format specifiers.
Second first, your strings file should contain:
"name %#" = "My name is %#"
so the key has the same format specifiers as the result (if you need to reorder the specifiers for a particular language you use positional format specifiers).
Now define a simple function to do the lookup, attributing it as a format translation function. Note we mark it as static inline, using the macro NS_INLINE as a hint to the compiler to both inline it into your macro expansion; the static allows you to include it in multiple files without symbol clashes:
NS_INLINE NSString *localize(NSString *string) NS_FORMAT_ARGUMENT(1);
NSString *localize(NSString *string)
{
return [[NSBundle mainBundle] localizedStringForKey:string value:#"" table:nil];
}
And your macro becomes:
#define GetLocalStr(key, ...) [NSString stringWithFormat:localize(key), ##__VA_ARGS__]
Now when you:
GetLocalStr(#"name %#", #"Foo")
You will get both the localised format string and format checking.
Update
After Greg's comment I went back and checked - I had reproduced your error and so assumed it was down to a missing attribute. However as Greg points out localizedStringForKey:value:table: already has the attribute, so why the error? What I had absentmindedly done in reproducing your error was:
NSLog( GetLocalStr( #"name %#", #"Foo" ) );
and the compiler pointed at the macro definition and not that line - I should have spotted the compiler was misleading me.
So where does that leave you? Maybe you've done something similar? The key is that a format string must either be a literal or the result of a function/method attributed as a format translating function. And don't forget, you must also had the format specifier to your key as above.
Update 2
After your additional comments what you need to use is function, rather than a macro, along with the format attribute, for which Cocoa provides the convenient NS_FORMAT_FUNCTION(f,a) macro. This attribute informs the compiler that the function is a formatting one, the value of f is the number of the format string and a is the number of the first argument to the format. This gives the function declaration:
NSString *GetLocalStr(NSString *key, ...) NS_FORMAT_FUNCTION(1,2);
and the definition (assuming ARC):
NSString *GetLocalStr(NSString *key, ...)
{
va_list args;
va_start(args, key);
NSString *format = [[NSBundle mainBundle] localizedStringForKey:key value:#"" table:nil];
NSString *result = [[NSString alloc] initWithFormat:format arguments:args];
va_end (args);
return result;
}
(which is essentially the same as #A-Live's).
Uses of this will be checked appropriately, for example:
int x;
...
NSString *s1 = GetLocalStr(#"name = %d", x); // OK
NSString *s2 = GetLocalStr(#"name = %d"); // compile warning - More '%" conversions than data arguments
NSString *s3 = GetLocalStr(#"name", x); // compile warning - Data argument not used by format string
NSString *s4 = GetLocalStr(#"name"); // OK
This variant produces no warnings (as there's always a va_list):
NSString* GetLocalStr1(NSString *formatKey, ...) {
va_list ap;
va_start(ap, formatKey);
NSString * format = [[NSBundle mainBundle] localizedStringForKey:formatKey value:#"" table:nil];
NSString *result = [[NSString alloc] initWithFormat:format arguments:ap];
va_end (ap);
return [result autorelease];
}
...
__unused NSString * str = GetLocalStr1( #"name", #"Foo" );
__unused NSString * str1 = GetLocalStr1( #"TestNoArgs" );
__unused NSString * str2 = GetLocalStr1( #"TestArgs", #"Foo" );
NSLog(#"%#", str);
NSLog(#"%#", str1);
NSLog(#"%#", str2);
Result:
my name is Foo
TestNoArgs
Hello world Foo
It doesn't answer the question exactly but might help you to avoid warnings until the solution is found.

Resources