NSString addition what does this Objective-c code do? - objective-c

Ran across this NSString Addition and I hav no idea what it does or is used for?
NSString *NSStringWithFormat(NSString *formatString, ...) {
va_list args;
va_start(args, formatString);
NSString *string = [[NSString alloc] initWithFormat:formatString arguments:args];
va_end(args);
#if defined(__has_feature) && __has_feature(objc_arc)
return string;
#else
return [string autorelease];
#endif
}

It's a C function that lets you do this:
NSString *str = NSStringWithFormat(#"This is a number: %d", someIntValue);
instead of this:
// No ARC
NSString *str = [NSString stringWithFormat:#"This is a number: %d", someIntValue];
or
// With ARC
NSString *str = [[NSString alloc] initWithFormat:#"This is a number: %d", someIntValue];
Seems kind of pointless to me since with or without ARC you can use the "No ARC" code. This C function only saves a few characters.

Related

why is this for loop not getting called?

Hello and thanks for the help
Why is this for loop not getting called. (contents is an nsmutableArray)
NSString *setBiz = [[NSString alloc]init];
setBiz = #"MomAndPop";
NSLog(#"??????????listby???????????%#\n",setBiz);
for (NSDictionary *key in self.contents) {
NSLog(#"hi inside loopppp"); //I never see this ????????
NSString *c = [key objectForKey:#"BizName"];
NSString *string = [NSString stringWithFormat:#"%#", key]; //random test
if ([c isEqualToString:setBiz]) {
NSLog(#"gotch you");
}
}
The most likely answer is that self.contents has no elements inside of it.
Place this before your loop to output the number of elements in the loop:
NSLog(#"self.contents.count: %lu", self.contents.count);

EXC_BAD_ACESS error

I get that error EXC_BAD_ACESS at the following line:
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionTitle",i];
Here is the for loop where the above code line is located:
for (i=0; i < count; ++i)
{
//Save the occasionS details to NSUserDefaults
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionTitle",i];
NSString *dateVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionDate",i];
NSString *imageVarName = [[NSString alloc] initWithFormat:#"%#%#",#"occasionImage",i];
[[NSUserDefaults standardUserDefaults] setValue:[[[self displayedObjects] objectAtIndex:i]
title] forKey:titleVarName];
[[NSUserDefaults standardUserDefaults] setValue:[[[self displayedObjects] objectAtIndex:i]
date] forKey:dateVarName];
[[NSUserDefaults standardUserDefaults] setValue:[[[self displayedObjects] objectAtIndex:i]
imagePath] forKey:imageVarName];
//release
[titleVarName release];
[dateVarName release];
[imageVarName release];
[self dismissModalViewControllerAnimated:YES];
}
Isn't ok to alloc objects and release them inside a for loop?
You need to use %d or %i specifier instead of %# to specify an integer. If %# is used with int then it will try to access the object at the address specified by the int. For example, if the value of i is one then it is trying to access the object at address one which will cause a bad access.
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%d",#"occasionTitle",i];
And also you don't need alloc and release here, though that is not the reason of bad access. You can use a convenience constructor.
NSString *titleVarName = [NSString stringWithFormat:#"occasionTitle%d", i];
// release not required
Do the same for dateVarName and imageVarName too.
Assuming i is an int, that line should be
NSString *titleVarName = [[NSString alloc] initWithFormat:#"%#%i",#"occasionTitle",i];
%# is used for Cocoa objects, not primitives like an int, float or bool;
Use the %# format specifier only for NSObject objects.
As i is an integer in your code, you have to use %d or %i for integers.
Moreover, there is no need to include the string using %#, you can use the static string directly in your format string:
NSString *titleVarName = [[NSString alloc] initWithFormat:#"occasionTitle%i",i];

Append a NSString as the first line of another NSString

I have two NSString, A and B.
I would that A becomes B\nA.
How can I do?
If in a method I use
NSString *string_B = [[NSString alloc] initWithString:#"something_from_a_DB"];
NSString *string_A = [[NSString alloc] initWithString:#"something_from_a_DB"];
if (aTrueCondition) {
string_C = [NSString stringWithFormat:#"%#\n%#", string_B, string_A];
} else {
string_C = string_A;
}
is string_C = string_A; a memory leak or is it good?
I added [string_A release], as string_C is a retained property. Now it works.
This is the way to put them together:
NSString *newString = [NSString stringWithFormat:#"%#\n%#", stringB, stringA];
The second part is “A becoming newString”. This is hard to do, as regular strings are immutable in Cocoa. The best thing you can do is throw out the old A and point A to the new string:
NSString *strA = #"foo";
NSString *strB = #"bar";
strA = [NSString stringWith…];
Just be careful not to leak A:
NSString *strA = [[NSString alloc] initWithString:#"foo"];
strA = [NSString stringWith…]; // this is a leak
NSString *str=[NSString stringWithFormat:#"%#\n%#",B,A];
use this.
NSString *stringA = [NSString stringWithFormat:#"%#\n%#", stringB, stringA];

How to create a NSString from a format string like #"xxx=%#, yyy=%#" and a NSArray of objects?

Is there any way to create a new
NSString from a format string like #"xxx=%#, yyy=%#" and a NSArray of objects?
In the NSSTring class there are many methods like:
- (id)initWithFormat:(NSString *)format arguments:(va_list)argList
- (id)initWithFormat:(NSString *)format locale:(id)locale arguments:(va_list)argList
+ (id)stringWithFormat:(NSString *)format, ...
but non of them takes a NSArray as an argument, and I cannot find a way to create a va_list from a NSArray...
It is actually not hard to create a va_list from an NSArray. See Matt Gallagher's excellent article on the subject.
Here is an NSString category to do what you want:
#interface NSString (NSArrayFormatExtension)
+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments;
#end
#implementation NSString (NSArrayFormatExtension)
+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments
{
char *argList = (char *)malloc(sizeof(NSString *) * arguments.count);
[arguments getObjects:(id *)argList];
NSString* result = [[[NSString alloc] initWithFormat:format arguments:argList] autorelease];
free(argList);
return result;
}
#end
Then:
NSString* s = [NSString stringWithFormat:#"xxx=%#, yyy=%#" array:#[#"XXX", #"YYY"]];
NSLog( #"%#", s );
Unfortunately, for 64-bit, the va_list format has changed, so the above code no longer works. And probably should not be used anyway given it depends on the format that is clearly subject to change. Given there is no really robust way to create a va_list, a better solution is to simply limit the number of arguments to a reasonable maximum (say 10) and then call stringWithFormat with the first 10 arguments, something like this:
+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments
{
if ( arguments.count > 10 ) {
#throw [NSException exceptionWithName:NSRangeException reason:#"Maximum of 10 arguments allowed" userInfo:#{#"collection": arguments}];
}
NSArray* a = [arguments arrayByAddingObjectsFromArray:#[#"X",#"X",#"X",#"X",#"X",#"X",#"X",#"X",#"X",#"X"]];
return [NSString stringWithFormat:format, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9] ];
}
Based on this answer using Automatic Reference Counting (ARC): https://stackoverflow.com/a/8217755/881197
Add a category to NSString with the following method:
+ (id)stringWithFormat:(NSString *)format array:(NSArray *)arguments
{
NSRange range = NSMakeRange(0, [arguments count]);
NSMutableData *data = [NSMutableData dataWithLength:sizeof(id) * [arguments count]];
[arguments getObjects:(__unsafe_unretained id *)data.mutableBytes range:range];
NSString *result = [[NSString alloc] initWithFormat:format arguments:data.mutableBytes];
return result;
}
One solution that came to my mind is that I could create a method that works with a fixed large number of arguments like:
+ (NSString *) stringWithFormat: (NSString *) format arguments: (NSArray *) arguments {
return [NSString stringWithFormat: format ,
(arguments.count>0) ? [arguments objectAtIndex: 0]: nil,
(arguments.count>1) ? [arguments objectAtIndex: 1]: nil,
(arguments.count>2) ? [arguments objectAtIndex: 2]: nil,
...
(arguments.count>20) ? [arguments objectAtIndex: 20]: nil];
}
I could also add a check to see if the format string has more than 21 '%' characters and throw an exception in that case.
#Chuck is correct about the fact that you can't convert an NSArray into varargs. However, I don't recommend searching for the pattern %# in the string and replacing it each time. (Replacing characters in the middle of a string is generally quite inefficient, and not a good idea if you can accomplish the same thing in a different way.) Here is a more efficient way to create a string with the format you're describing:
NSArray *array = ...
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:[array count]];
for (id object in array) {
[newArray addObject:[NSString stringWithFormat:#"x=%#", [object description]]];
}
NSString *composedString = [[newArray componentsJoinedByString:#", "] retain];
[pool drain];
I included the autorelease pool for good housekeeping, since an autoreleased string will be created for each array entry, and the mutable array is autoreleased as well. You could easily make this into a method/function and return composedString without retaining it, and handle the autorelease elsewhere in the code if desired.
This answer is buggy. As noted, there is no solution to this problem that is guaranteed to work when new platforms are introduced other than using the "10 element array" method.
The answer by solidsun was working well, until I went to compile with 64-bit architecture. This caused an error:
EXC_BAD_ADDRESS type EXC_I386_GPFLT
The solution was to use a slightly different approach for passing the argument list to the method:
+ (id)stringWithFormat:(NSString *)format array:(NSArray*) arguments;
{
__unsafe_unretained id * argList = (__unsafe_unretained id *) calloc(1UL, sizeof(id) * arguments.count);
for (NSInteger i = 0; i < arguments.count; i++) {
argList[i] = arguments[i];
}
NSString* result = [[NSString alloc] initWithFormat:format, *argList] ;// arguments:(void *) argList];
free (argList);
return result;
}
This only works for arrays with a single element
There is no general way to pass an array to a function or method that uses varargs. In this particular case, however, you could fake it by using something like:
for (NSString *currentReplacement in array)
[string stringByReplacingCharactersInRange:[string rangeOfString:#"%#"]
withString:currentReplacement];
EDIT: The accepted answer claims there is a way to do this, but regardless of how fragile this answer might seem, that approach is far more fragile. It relies on implementation-defined behavior (specifically, the structure of a va_list) that is not guaranteed to remain the same. I maintain that my answer is correct and my proposed solution is less fragile since it only relies on defined features of the language and frameworks.
For those who need a Swift solution, here is an extension to do this in Swift
extension String {
static func stringWithFormat(format: String, argumentsArray: Array<AnyObject>) -> String {
let arguments = argumentsArray.map { $0 as! CVarArgType }
let result = String(format:format, arguments:arguments)
return result
}
}
Yes, it is possible. In GCC targeting Mac OS X, at least, va_list is simply a C array, so you'll make one of ids, then tell the NSArray to fill it:
NSArray *argsArray = [[NSProcessInfo processInfo] arguments];
va_list args = malloc(sizeof(id) * [argsArray count]);
NSAssert1(args != nil, #"Couldn't allocate array for %u arguments", [argsArray count]);
[argsArray getObjects:(id *)args];
//Example: NSLogv is the version of NSLog that takes a va_list instead of separate arguments.
NSString *formatSpecifier = #"\n%#";
NSString *format = [#"Arguments:" stringByAppendingString:[formatSpecifier stringByPaddingToLength:[argsArray count] * 3U withString:formatSpecifier startingAtIndex:0U]];
NSLogv(format, args);
free(args);
You shouldn't rely on this nature in code that should be portable. iPhone developers, this is one thing you should definitely test on the device.
- (NSString *)stringWithFormat:(NSString *)format andArguments:(NSArray *)arguments {
NSMutableString *result = [NSMutableString new];
NSArray *components = format ? [format componentsSeparatedByString:#"%#"] : #[#""];
NSUInteger argumentsCount = [arguments count];
NSUInteger componentsCount = [components count] - 1;
NSUInteger iterationCount = argumentsCount < componentsCount ? argumentsCount : componentsCount;
for (NSUInteger i = 0; i < iterationCount; i++) {
[result appendFormat:#"%#%#", components[i], arguments[i]];
}
[result appendString:[components lastObject]];
return iterationCount == 0 ? [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] : result;
}
Tested with format and arguments:
NSString *format = #"xxx=%#, yyy=%# last component";
NSArray *arguments = #[#"XXX", #"YYY", #"ZZZ"];
Result: xxx=XXX, yyy=YYY last component
NSString *format = #"xxx=%#, yyy=%# last component";
NSArray *arguments = #[#"XXX", #"YYY"];
Result: xxx=XXX, yyy=YYY last component
NSString *format = #"xxx=%#, yyy=%# last component";
NSArray *arguments = #[#"XXX"];
Result: xxx=XXX last component
NSString *format = #"xxx=%#, yyy=%# last component";
NSArray *arguments = #[];
Result: last component
NSString *format = #"some text";
NSArray *arguments = #[#"XXX", #"YYY", #"ZZZ"];
Result: some text
I found some code on the web that claims that this is possible however I haven't managed to do it myself, however if you don't know the number of arguments in advance you also need to build the format string dynamically so I just don't see the point.
You better off just building the string by iterating the array.
You might find the stringByAppendingString: or stringByAppendingFormat: instance method handy .
One can create a category for NSString and make a function which receives a format, an array and returns the string with replaced objects.
#interface NSString (NSArrayFormat)
+ (NSString *)stringWithFormat:(NSString *)format arrayArguments:(NSArray *)arrayArguments;
#end
#implementation NSString (NSArrayFormat)
+ (NSString *)stringWithFormat:(NSString *)format arrayArguments:(NSArray *)arrayArguments {
static NSString *objectSpecifier = #"%#"; // static is redundant because compiler will optimize this string to have same address
NSMutableString *string = [[NSMutableString alloc] init]; // here we'll create the string
NSRange searchRange = NSMakeRange(0, [format length]);
NSRange rangeOfPlaceholder = NSMakeRange(NSNotFound, 0); // variables are declared here because they're needed for NSAsserts
NSUInteger index;
for (index = 0; index < [arrayArguments count]; ++index) {
rangeOfPlaceholder = [format rangeOfString:objectSpecifier options:0 range:searchRange]; // find next object specifier
if (rangeOfPlaceholder.location != NSNotFound) { // if we found one
NSRange substringRange = NSMakeRange(searchRange.location, rangeOfPlaceholder.location - searchRange.location);
NSString *formatSubstring = [format substringWithRange:substringRange];
[string appendString:formatSubstring]; // copy the format from previous specifier up to this one
NSObject *object = [arrayArguments objectAtIndex:index];
NSString *objectDescription = [object description]; // convert object into string
[string appendString:objectDescription];
searchRange.location = rangeOfPlaceholder.location + [objectSpecifier length]; // update the search range in order to minimize search
searchRange.length = [format length] - searchRange.location;
} else {
break;
}
}
if (rangeOfPlaceholder.location != NSNotFound) { // we need to check if format still specifiers
rangeOfPlaceholder = [format rangeOfString:#"%#" options:0 range:searchRange];
}
NSAssert(rangeOfPlaceholder.location == NSNotFound, #"arrayArguments doesn't have enough objects to fill specified format");
NSAssert(index == [arrayArguments count], #"Objects starting with index %lu from arrayArguments have been ignored because there aren't enough object specifiers!", index);
return string;
}
#end
Because NSArray is created at runtime we cannot provide compile-time warnings, but we can use NSAssert to tell us if number of specifiers is equal with number of objects within array.
Created a project on Github where this category can be found. Also added Chuck's version by using 'stringByReplacingCharactersInRange:' plus some tests.
Using one million objects into array, version with 'stringByReplacingCharactersInRange:' doesn't scale very well (waited about 2 minutes then closed the app). Using the version with NSMutableString, function made the string in about 4 seconds. The tests were made using simulator. Before usage, tests should be done on a real device (use a device with lowest specs).
Edit: On iPhone 5s the version with NSMutableString takes 10.471655s (one million objects); on iPhone 5 takes 21.304876s.
Here's the answer without explicitly creating an array:
NSString *formattedString = [NSString stringWithFormat:#"%# World, Nice %#", #"Hello", #"Day"];
First String is the target string to be formatted, the next string are the string to be inserted in the target.
No, you won't be able to. Variable argument calls are solved at compile time, and your NSArray has contents only at runtime.

Shortcuts in Objective-C to concatenate NSStrings

Are there any shortcuts to (stringByAppendingString:) string concatenation in Objective-C, or shortcuts for working with NSString in general?
For example, I'd like to make:
NSString *myString = #"This";
NSString *test = [myString stringByAppendingString:#" is just a test"];
something more like:
string myString = "This";
string test = myString + " is just a test";
An option:
[NSString stringWithFormat:#"%#/%#/%#", one, two, three];
Another option:
I'm guessing you're not happy with multiple appends (a+b+c+d), in which case you could do:
NSLog(#"%#", [Util append:one, #" ", two, nil]); // "one two"
NSLog(#"%#", [Util append:three, #"/", two, #"/", one, nil]); // three/two/one
using something like
+ (NSString *) append:(id) first, ...
{
NSString * result = #"";
id eachArg;
va_list alist;
if(first)
{
result = [result stringByAppendingString:first];
va_start(alist, first);
while (eachArg = va_arg(alist, id))
result = [result stringByAppendingString:eachArg];
va_end(alist);
}
return result;
}
Two answers I can think of... neither is particularly as pleasant as just having a concatenation operator.
First, use an NSMutableString, which has an appendString method, removing some of the need for extra temp strings.
Second, use an NSArray to concatenate via the componentsJoinedByString method.
If you have 2 NSString literals, you can also just do this:
NSString *joinedFromLiterals = #"ONE " #"MILLION " #"YEARS " #"DUNGEON!!!";
That's also useful for joining #defines:
#define STRINGA #"Also, I don't know "
#define STRINGB #"where food comes from."
#define JOINED STRINGA STRINGB
Enjoy.
I keep returning to this post and always end up sorting through the answers to find this simple solution that works with as many variables as needed:
[NSString stringWithFormat:#"%#/%#/%#", three, two, one];
For example:
NSString *urlForHttpGet = [NSString stringWithFormat:#"http://example.com/login/username/%#/userid/%i", userName, userId];
Create a method:
- (NSString *)strCat: (NSString *)one: (NSString *)two
{
NSString *myString;
myString = [NSString stringWithFormat:#"%#%#", one , two];
return myString;
}
Then, in whatever function you need it in, set your string or text field or whatever to the return value of this function.
Or, to make a shortcut, convert the NSString into a C++ string and use the '+' there.
Well, as colon is kind of special symbol, but is part of method signature, it is possible to exted the NSString with category to add this non-idiomatic style of string concatenation:
[#"This " : #"feels " : #"almost like " : #"concatenation with operators"];
You can define as many colon separated arguments as you find useful... ;-)
For a good measure, I've also added concat: with variable arguments that takes nil terminated list of strings.
// NSString+Concatenation.h
#import <Foundation/Foundation.h>
#interface NSString (Concatenation)
- (NSString *):(NSString *)a;
- (NSString *):(NSString *)a :(NSString *)b;
- (NSString *):(NSString *)a :(NSString *)b :(NSString *)c;
- (NSString *):(NSString *)a :(NSString *)b :(NSString *)c :(NSString *)d;
- (NSString *)concat:(NSString *)strings, ...;
#end
// NSString+Concatenation.m
#import "NSString+Concatenation.h"
#implementation NSString (Concatenation)
- (NSString *):(NSString *)a { return [self stringByAppendingString:a];}
- (NSString *):(NSString *)a :(NSString *)b { return [[self:a]:b];}
- (NSString *):(NSString *)a :(NSString *)b :(NSString *)c
{ return [[[self:a]:b]:c]; }
- (NSString *):(NSString *)a :(NSString *)b :(NSString *)c :(NSString *)d
{ return [[[[self:a]:b]:c]:d];}
- (NSString *)concat:(NSString *)strings, ...
{
va_list args;
va_start(args, strings);
NSString *s;
NSString *con = [self stringByAppendingString:strings];
while((s = va_arg(args, NSString *)))
con = [con stringByAppendingString:s];
va_end(args);
return con;
}
#end
// NSString+ConcatenationTest.h
#import <SenTestingKit/SenTestingKit.h>
#import "NSString+Concatenation.h"
#interface NSString_ConcatenationTest : SenTestCase
#end
// NSString+ConcatenationTest.m
#import "NSString+ConcatenationTest.h"
#implementation NSString_ConcatenationTest
- (void)testSimpleConcatenation
{
STAssertEqualObjects([#"a":#"b"], #"ab", nil);
STAssertEqualObjects([#"a":#"b":#"c"], #"abc", nil);
STAssertEqualObjects([#"a":#"b":#"c":#"d"], #"abcd", nil);
STAssertEqualObjects([#"a":#"b":#"c":#"d":#"e"], #"abcde", nil);
STAssertEqualObjects([#"this " : #"is " : #"string " : #"concatenation"],
#"this is string concatenation", nil);
}
- (void)testVarArgConcatenation
{
NSString *concatenation = [#"a" concat:#"b", nil];
STAssertEqualObjects(concatenation, #"ab", nil);
concatenation = [concatenation concat:#"c", #"d", concatenation, nil];
STAssertEqualObjects(concatenation, #"abcdab", nil);
}
Use stringByAppendingString: this way:
NSString *string1, *string2, *result;
string1 = #"This is ";
string2 = #"my string.";
result = [result stringByAppendingString:string1];
result = [result stringByAppendingString:string2];
OR
result = [result stringByAppendingString:#"This is "];
result = [result stringByAppendingString:#"my string."];
Macro:
// stringConcat(...)
// A shortcut for concatenating strings (or objects' string representations).
// Input: Any number of non-nil NSObjects.
// Output: All arguments concatenated together into a single NSString.
#define stringConcat(...) \
[#[__VA_ARGS__] componentsJoinedByString:#""]
Test Cases:
- (void)testStringConcat {
NSString *actual;
actual = stringConcat(); //might not make sense, but it's still a valid expression.
STAssertEqualObjects(#"", actual, #"stringConcat");
actual = stringConcat(#"A");
STAssertEqualObjects(#"A", actual, #"stringConcat");
actual = stringConcat(#"A", #"B");
STAssertEqualObjects(#"AB", actual, #"stringConcat");
actual = stringConcat(#"A", #"B", #"C");
STAssertEqualObjects(#"ABC", actual, #"stringConcat");
// works on all NSObjects (not just strings):
actual = stringConcat(#1, #" ", #2, #" ", #3);
STAssertEqualObjects(#"1 2 3", actual, #"stringConcat");
}
Alternate macro: (if you wanted to enforce a minimum number of arguments)
// stringConcat(...)
// A shortcut for concatenating strings (or objects' string representations).
// Input: Two or more non-nil NSObjects.
// Output: All arguments concatenated together into a single NSString.
#define stringConcat(str1, str2, ...) \
[#[ str1, str2, ##__VA_ARGS__] componentsJoinedByString:#""];
When building requests for web services, I find doing something like the following is very easy and makes concatenation readable in Xcode:
NSString* postBody = {
#"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
#"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
#" <soap:Body>"
#" <WebServiceMethod xmlns=\"\">"
#" <parameter>test</parameter>"
#" </WebServiceMethod>"
#" </soap:Body>"
#"</soap:Envelope>"
};
Shortcut by creating AppendString (AS) macro ...
#define AS(A,B) [(A) stringByAppendingString:(B)]
NSString *myString = #"This";
NSString *test = AS(myString,#" is just a test");
Note:
If using a macro, of course just do it with variadic arguments, see EthanB's answer.
NSString *label1 = #"Process Name: ";
NSString *label2 = #"Process Id: ";
NSString *processName = [[NSProcessInfo processInfo] processName];
NSString *processID = [NSString stringWithFormat:#"%d", [[NSProcessInfo processInfo] processIdentifier]];
NSString *testConcat = [NSString stringWithFormat:#"%# %# %# %#", label1, processName, label2, processID];
Here's a simple way, using the new array literal syntax:
NSString * s = [#[#"one ", #"two ", #"three"] componentsJoinedByString:#""];
^^^^^^^ create array ^^^^^
^^^^^^^ concatenate ^^^^^
NSString *myString = #"This";
NSString *test = [myString stringByAppendingString:#" is just a test"];
After a couple of years now with Objective C I think this is the best way to work with Objective C to achieve what you are trying to achieve.
Start keying in "N" in your Xcode application and it autocompletes to "NSString".
key in "str" and it autocompletes to "stringByAppendingString". So the keystrokes are quite limited.
Once you get the hang of hitting the "#" key and tabbing the process of writing readable code no longer becomes a problem. It is just a matter of adapting.
The only way to make c = [a stringByAppendingString: b] any shorter is to use autocomplete at around the st point. The + operator is part of C, which doesn't know about Objective-C objects.
How about shortening stringByAppendingString and use a #define:
#define and stringByAppendingString
Thus you would use:
NSString* myString = [#"Hello " and #"world"];
Problem is that it only works for two strings, you're required to wrap additional brackets for more appends:
NSString* myString = [[#"Hello" and: #" world"] and: #" again"];
NSString *result=[NSString stringWithFormat:#"%# %#", #"Hello", #"World"];
NSString *label1 = #"Process Name: ";
NSString *label2 = #"Process Id: ";
NSString *processName = [[NSProcessInfo processInfo] processName];
NSString *processID = [NSString stringWithFormat:#"%d", [[NSProcessInfo processInfo] processIdentifier]];
NSString *testConcat = [NSString stringWithFormat:#"%# %# %# %#", label1, processName, label2, processID];
I tried this code. it's worked for me.
NSMutableString * myString=[[NSMutableString alloc]init];
myString=[myString stringByAppendingString:#"first value"];
myString=[myString stringByAppendingString:#"second string"];
Was trying the following in the lldb pane
[NSString stringWithFormat:#"%#/%#/%#", three, two, one];
which errors.
instead use alloc and initWithFormat method:
[[NSString alloc] initWithFormat:#"%#/%#/%#", #"three", #"two", #"one"];
This is for better logging, and logging only - based on dicius excellent multiple argument method. I define a Logger class, and call it like so:
[Logger log: #"foobar ", #" asdads ", theString, nil];
Almost good, except having to end the var args with "nil" but I suppose there's no way around that in Objective-C.
Logger.h
#interface Logger : NSObject {
}
+ (void) log: (id) first, ...;
#end
Logger.m
#implementation Logger
+ (void) log: (id) first, ...
{
// TODO: make efficient; handle arguments other than strings
// thanks to #diciu http://stackoverflow.com/questions/510269/how-do-i-concatenate-strings-in-objective-c
NSString * result = #"";
id eachArg;
va_list alist;
if(first)
{
result = [result stringByAppendingString:first];
va_start(alist, first);
while (eachArg = va_arg(alist, id))
{
result = [result stringByAppendingString:eachArg];
}
va_end(alist);
}
NSLog(#"%#", result);
}
#end
In order to only concat strings, I'd define a Category on NSString and add a static (+) concatenate method to it that looks exactly like the log method above except it returns the string. It's on NSString because it's a string method, and it's static because you want to create a new string from 1-N strings, not call it on any one of the strings that are part of the append.
NSNumber *lat = [NSNumber numberWithDouble:destinationMapView.camera.target.latitude];
NSNumber *lon = [NSNumber numberWithDouble:destinationMapView.camera.target.longitude];
NSString *DesconCatenated = [NSString stringWithFormat:#"%#|%#",lat,lon];
Try stringWithFormat:
NSString *myString = [NSString stringWithFormat:#"%# %# %# %d", "The", "Answer", "Is", 42];
When dealing with strings often I find it easier to make the source file ObjC++, then I can concatenate std::strings using the second method shown in the question.
std::string stdstr = [nsstr UTF8String];
//easier to read and more portable string manipulation goes here...
NSString* nsstr = [NSString stringWithUTF8String:stdstr.c_str()];
My preferred method is this:
NSString *firstString = #"foo";
NSString *secondString = #"bar";
NSString *thirdString = #"baz";
NSString *joinedString = [#[firstString, secondString, thirdString] join];
You can achieve it by adding the join method to NSArray with a category:
#import "NSArray+Join.h"
#implementation NSArray (Join)
-(NSString *)join
{
return [self componentsJoinedByString:#""];
}
#end
#[] it's the short definition for NSArray, I think this is the fastest method to concatenate strings.
If you don't want to use the category, use directly the componentsJoinedByString: method:
NSString *joinedString = [#[firstString, secondString, thirdString] componentsJoinedByString:#""];
You can use NSArray as
NSString *string1=#"This"
NSString *string2=#"is just"
NSString *string3=#"a test"
NSArray *myStrings = [[NSArray alloc] initWithObjects:string1, string2, string3,nil];
NSString *fullLengthString = [myStrings componentsJoinedByString:#" "];
or
you can use
NSString *imageFullName=[NSString stringWithFormat:#"%# %# %#.", string1,string2,string3];
Either of these formats work in XCode7 when I tested:
NSString *sTest1 = {#"This" " and that" " and one more"};
NSString *sTest2 = {
#"This"
" and that"
" and one more"
};
NSLog(#"\n%#\n\n%#",sTest1,sTest2);
For some reason, you only need the # operator character on the first string of the mix.
However, it doesn't work with variable insertion. For that, you can use this extremely simple solution with the exception of using a macro on "cat" instead of "and".
For all Objective C lovers that need this in a UI-Test:
-(void) clearTextField:(XCUIElement*) textField{
NSString* currentInput = (NSString*) textField.value;
NSMutableString* deleteString = [NSMutableString new];
for(int i = 0; i < currentInput.length; ++i) {
[deleteString appendString: [NSString stringWithFormat:#"%c", 8]];
}
[textField typeText:deleteString];
}
listOfCatalogIDs =[#[#"id[]=",listOfCatalogIDs] componentsJoinedByString:#""];
Let's imagine that u don't know how many strings there.
NSMutableArray *arrForStrings = [[NSMutableArray alloc] init];
for (int i=0; i<[allMyStrings count]; i++) {
NSString *str = [allMyStrings objectAtIndex:i];
[arrForStrings addObject:str];
}
NSString *readyString = [[arrForStrings mutableCopy] componentsJoinedByString:#", "];
Inspired by NSMutableString idea from Chris, I make a perfect macro imho.
It supports insert nil elements without any Exceptions.
#import <libextobjc/metamacros.h>
#define STR_CONCAT(...) \
({ \
__auto_type str__ = [NSMutableString string]; \
metamacro_foreach_cxt(never_use_immediately_str_concatify_,, str__, __VA_ARGS__) \
(NSString *)str__.copy; \
})
#define never_use_immediately_str_concatify_(INDEX, CONTEXT, VAR) \
[CONTEXT appendString:VAR ?: #""];
Example:
STR_CONCAT(#"button_bg_", #(count).stringValue, #".png");
// button_bg_2.png
If you like, you can use id type as parameter by using [VAR description] instead of NSString.