Objective C - #define using __VA_ARGS__ - ios

I am learning how to use macro but now confused with one.
I am trying to create a NSString concatenate which will just append every params to each other.
for example : concatOP(#"hey",#"Jude",#"Don't") would return a NSString containing : #"heyJudeDon't"
I actually made a bit of code (some found here as well) which get the number of params but I don't succeed to make the second part of the job.
#define NUMARGS(...) ( sizeof((int[]){__VA_ARGS__}) / sizeof(int) )
#define concatOP(...) NSMutableString *format = [[NSMutableString alloc] init];\
for( int i = 0; i < NUMARGS(__VA_ARGS__); i++){\
[format appendString:#"%#"];}\
[[NSString alloc] initWithFormat:format, __VA_ARGS__]
I actually get many errors, telling me that format doesn't exist or that I miss some ";" or other ending tags.

Here is yours macro:
#define concatOP(...) [#[__VA_ARGS__] componentsJoinedByString:#""]
EDIT:
if you unwind yours macro NSString* result = concatOP(#"hey",#"Jude",#"Don't");
you will get:
NSString* result = NSMutableString *format = [[NSMutableString alloc] init]; for( int i = 0; i < NUMARGS(#"hey",#"Jude",#"Don't"); i++){ format = [format appendString:#"%#"];} [[NSString alloc] initWithFormat:format, #"hey",#"Jude",#"Don't"];
Looks odd.

This doesn't exactly answer your question, but NSString literals are concatenated by the compiler, just like their C-counterparts, so this code works out of the box:
NSString *str = #"Hey" #"Jude" #"Don't";
which is the same as:
NSString *str = #"HeyJudeDon't";
This is typically used to split a long string literal across multiple lines of the source file.
Bottom line; you don't need all those messy macros and pointless methods to do this.

I don't know how to do this with macros.
You can do it in Objective C like:
Implement a method like:
- (NSString *)concateStrings:(NSString *)firstArg, ...
{
NSMutableString *concatString = [[NSMutableString alloc] init];
va_list args;
va_start(args, firstArg);
for (NSString *arg = firstArg; arg != nil; arg = va_arg(args, NSString*))
{
[concatString appendString:arg];
}
va_end(args);
return concatString;
}
You can call this method like:
NSLog(#"%#",[self concateStrings:#"hey",#"Jude",#"Don't",nil]) ;
Output:
heyJudeDon't
Make sure that you pass nil at the end.

Related

Ways to build NSStrings in Objective-C? [duplicate]

C# has syntax that allows you to specify the argument index in a string format specifier, e.g.:
string message = string.Format("Hello, {0}. You are {1} years old. How does it feel to be {1}?", name, age);
You can use arguments more than once and also omit arguments that are provided from being used. Another question mentions the same formatting for C/C++ in the form of %[index]$[format], e.g. %1$i. I have not been able to get NSString to fully respect this syntax, because it does behave well when omitting arguments from the format. The following does not work as expected (EXC_BAD_ACCESS because it attempts to dereference the age parameter as a NSObject*):
int age = 23;
NSString * name = #"Joe";
NSString * message = [NSString stringWithFormat:#"Age: %2$i", name, age];
The positional arguments are respected only if there are no missing arguments from the format (which seems to be an odd requirement):
NSString * message = [NSString stringWithFormat:#"Age: %2$i; Name: %1$#", name, age];
All these calls work properly in OS X:
printf("Age: %2$i", [name UTF8String], age);
printf("Age: %2$i %1$s", [name UTF8String], age);
Is there a way of accomplishing this using NSString in Objective-C / Cocoa? It would be useful for localization purposes.
NSString and CFString support reorderable/positional arguments.
NSString *string = [NSString stringWithFormat: #"Second arg: %2$#, First arg %1$#", #"<1111>", #"<22222>"];
NSLog(#"String = %#", string);
Also, see the documentation at Apple: String Resources
The following code fixes the bug specified in this issue. It is a workaround and renumbers the placeholders to fill gaps.
+ (id)stringWithFormat:(NSString *)format arguments:(NSArray*) arguments
{
NSMutableArray *filteredArguments = [[NSMutableArray alloc] initWithCapacity:arguments.count];
NSMutableString *correctedFormat = [[NSMutableString alloc ] initWithString:format];
NSString *placeHolderFormat = #"%%%d$";
int actualPlaceholderIndex = 1;
for (int i = 1; i <= arguments.count; ++i) {
NSString *placeHolder = [[NSString alloc] initWithFormat:placeHolderFormat, i];
if ([format rangeOfString:placeHolder].location != NSNotFound) {
[filteredArguments addObject:[arguments objectAtIndex:i - 1]];
if (actualPlaceholderIndex != i) {
NSString *replacementPlaceHolder = [[NSString alloc] initWithFormat:placeHolderFormat, actualPlaceholderIndex];
[correctedFormat replaceAllOccurrencesOfString:placeHolder withString:replacementPlaceHolder];
[replacementPlaceHolder release];
}
actualPlaceholderIndex++;
}
[placeHolder release];
}
if (filteredArguments.count == 0) {
//No numbered arguments found: just copy the original arguments. Mixing of unnumbered and numbered arguments is not supported.
[filteredArguments setArray:arguments];
}
NSString* result;
if (filteredArguments.count == 0) {
//Still no arguments: don't use initWithFormat in this case because it will crash: just return the format string
result = [NSString stringWithString:format];
} else {
char *argList = (char *)malloc(sizeof(NSString *) * [filteredArguments count]);
[filteredArguments getObjects:(id *)argList];
result = [[[NSString alloc] initWithFormat:correctedFormat arguments:argList] autorelease];
free(argList);
}
[filteredArguments release];
[correctedFormat release];
return result;
}
After doing more research, it appears Cocoa respects positional syntax in printf. Therefore an alternate pattern would be:
char msg[512] = {0};
NSString * format = #"Age %2$i, Name: %1$s"; // loaded from resource in practice
sprintf(msg, [format UTF8String], [name UTF8String], age);
NSString * message = [NSString stringWithCString:msg encoding:NSUTF8StringEncoding];
However, it would be nice if there was an implementation of this on NSString.

Append integers to Objective C variable names

I am new to Objective C. What my code currently does is create a new Card object, assigns properties to the object and then adds the Card object to the cards array. The value numberOfCards varies.
for (int i = 0; i < numberOfCards; i++) {
Card *addCard = [Card new];
addCard.balance = [balanceArray objectAtIndex:i];
addCard.date = [dateArray objectAtIndex:i];
addCard.name = [nameArray objectAtIndex:i];
addCard.number = [numberArray objectAtIndex:i];
[cards addObject:addCard];
}
However, what I want to do is give each card a unique name. For example, if numberOfCards was n, then we would get the Card variable names of addCard1, addCard2 ... addCardn.
So how can I append i onto addCard?
Cheers
You mean like this?
NSString *cardName = [[NSString alloc] initWithFormat:#"addCard%i", i + 1];
addCard.name = cardName;
In order to chain a number to a string all you have to do is:
NSString *cardNewName = [NSString stringWithFormat:#"%#%i",cardName,cardNumber];
The idea is to build a format of string and then chain the necessary parameters.
%# is for string
%i is for integer
%f for float
etc.
It's better to use stringWithFormat then [[NSString alloc] initWithFormat:...] - for an extended information please take a look at the following question:
stringWithFormat vs. initWithFormat on NSString

Encrypt an NSString

I'd like to encrypt an NSString so that it isn't human-readable. The level of security doesnt manner (in other words, if somebody were to decrypt the text there wouldn't be any sensitive information for them to steal.
NSString *myTextToEncrypt = #"Hello World!";
[myTextToEncrypt encrypt];
// myTextToEncrypt is now something unreadable, like '2rwzdn1405'
Then I should be able to unencrypt this string
[myTextToEncrypt unencrypt]; // myTextToEncrypt should now be #"Hello World!" again
How do I do this? I've read some about CommonCrypto and AES Encryption but this all seems like overkill for what I'm trying to do (the encryption methods I've read are all for passwords or other sensitive pieces of data)
Simplest one is use your own encryption, e.g.
Utils.h
#interface Utils : NSObject
+(NSString*)encyptString:(NSString*)str;
+(NSString*)decryptString:(NSString*)str;
#end
Utils.m
#import "Utils.h"
int offset = 15;
#implementation Utils
+(NSString*)encyptString:(NSString*)str
{
NSMutableString *encrptedString = [[NSMutableString alloc] init];
for (int i = 0; i < str.length; i++) {
unichar character = [str characterAtIndex:i];
character += offset;
[encrptedString appendFormat:#"%C",character];
}
return encrptedString;
}
+(NSString*)decryptString:(NSString*)str
{
NSMutableString *decrptedString = [[NSMutableString alloc] init];
for (int i = 0; i < str.length; i++) {
unichar character = [str characterAtIndex:i];
character -= offset;
[decrptedString appendFormat:#"%C",character];
}
return decrptedString;
}
#end
How to use it
NSString *str = #"hello world";
NSString *enr = [Utils encyptString:str];
NSLog(#"Encrypted Text=%#", enr);
NSLog(#"Decrypted Text=%#", [Utils decryptString:enr]);
Logs
2013-08-11 10:44:09.409 DeviceTest[445:c07] Encrypted Text=wt{{~/~{s
2013-08-11 10:44:09.412 DeviceTest[445:c07] Decrypted Text=hello world
You can use base64 to do this.
There are some implementations in Objective-C available (as this one).
Note that the content will be about 30% bigger after it's encoded.

Create string based on characters expected

I would like to create a string based on the number of characters passed in. Each character passed in will be a "X". So for example, if the length passed in is 5, then the string created should be
NSString *testString=#"XXXXX";
if it is 2 then it would be
NSString *testString=#"XX";
Can anyone tell me what the most efficient way to do this would be?
Thank you!
If you know the maximum length is some reasonable number then you could do something simple like this:
- (NSString *)xString:(NSUInteger)length {
static NSString *xs = #"XXXXXXXXXXXXXXXXXXXXXXXXXXX";
return [xs substringToIndex:length];
}
NSString *str = [self xString:5]; // str will be #"XXXXX";
If you pass in too large of a length, the app will crash - add more Xs to xs.
This approach is more efficient than building up an NSMutableString but it does make an assumption about the maximum length you might need.
- (NSString *)stringOf:(NSString *)str times:(NSInteger)count
{
NSMutableString *targ = [[NSMutableString alloc] initWithCapacity:count];
for (int i=0; i < count; i++)
{
[targ appendString:str];
}
return targ;
}
and
[self stringOf:#"X" times:4];
note that initWithCapacity: (in performance manner) better than init. But I guess that's all for efficiency.
The way I would do it is
NSMutableString *xString = [[NSMutableString alloc] init];
while ( int i = 0; i < testString.length; i++ ) {
[xString appendString:#"X"];
i++;
}
NSUInteger aLength. // assume this is the argument
NSMutableString *xStr = [NSMutableString stringWithCapacity: aLength];
for ( NSUInteger i = 0; i < aLength; i++ ) {
[xStr appendFormat:#"X"];
}
The following will do what you ask in one call:
NSString *result = [#"" stringByPaddingToLength:numberOfCharsWanted
withString:characterToRepeat
startingAtIndex:0];
where numberOfCharsWanted is an NSUInteger and characterToRepeat is an NSString containing the character.

Extracting from the right of a string in objective C [duplicate]

This question already has answers here:
Get last 2 characters of a string?
(2 answers)
Closed 9 years ago.
This seems to be what I'm looking for but in reverse. I would like the string to extract from the right not from the left.
The example extracting from the left is given:
NSString *source = #"0123456789";
NSString *firstFour = [source substringToIndex:4];
Output: "0123"
I'm looking for a version of the below that works from the right (what is below doesn't work)
NSString *source = #"0123456789";
NSString *lastFour = [source substringToIndex:-4];
Output: "6789"
the [source substringFromIndex:6]; won't work because sometimes I will get an answer that is 000123456789 or 456789 or 6789. In all cases I just need the last 4 characters from the string so that I can convert it to a number.
there must be a better way than a bunch of if else statements?
As you are not sure, about the length of the string, so you must check it before extracting like this:
NSString *source = #"0123456789";
NSNumber *number;
if (source.length>=4) {
NSString *lastFour=[source substringFromIndex:source.length-4];
number=#([lastFour integerValue]); //and save it in a number, it can be int or NSInteger as per your need
}
NSLog(#"%#",number);
Also if you want a quick method that you need to call several times, create a category :
#implementation NSString (SubstringFromRight)
-(NSString *)substringFromRight:(NSUInteger)from{
if (self.length<from) {
return nil;
}
return [self substringFromIndex:self.length-from];
}
#end
And use it as :NSLog(#"%#",[source1 substringFromRight:4]);
NSString *source = #"0123456789";
NSString *newString = [source substringFromIndex:[source length] - 4];
NSLog(#"%#",newString);
replace
NSString *lastFour = [source substringToIndex:-4];
with
NSString *lastFour = [source substringFromIndex:[source length] - 4];
which returns you the last 4 characters of your original string string in lastFour string.
You can use the following code to get last 4 characters from your string.
NSString *last4Characters = [source substringFromIndex:(source.length - 4)];
NSLog(#"Last 4 Characters:%#",last4Characters);
last4Characters=nil;
Please let me know if any issue.

Resources