Iterate through variable number of arguments in function without passing nil - objective-c

I, like many, constantly have to look up date codes for date formatter. I decided to make a file that will make it easier for me to remember them all. I include a function for readability that I declare like this:
NSString * dateFormatString(NSString * string1, ...) {
// Parse out Args
va_list args;
va_start(args,string1);
// Set up our Format String
NSMutableString * formatString = [NSMutableString string];
// Build Format string
for (NSString * arg = string1; arg != nil; arg = va_arg(args, NSString*)) {
[formatString appendString:arg];
}
va_end(args);
return formatString;
}
So, I can then program my NSDateFormatter like this:
dateFormatter.dateFormatString = dateFormatString(DKDayOfWeekFull, #", ", DKMonthNameFull, #" ", DKDayOfMonthComplete, nil);
You could do achieve pretty much the same thing by declaring:
[NSString stringWithFormat:#"%#, %# %#", DKDayOfWeekFull, DKMonthNameFull, DKDayOfMonthComplete];
However, if you're describing a date with more variables like "Sat, Jan 14 2006 at 7:52 AM" it would have to be:
NSString * dateFormatterString = [NSString stringWithFormat:#"%#, %# %# %# 'at' %#:%# %#", DKDayOfWeekAbbreviated, DKMonthNameAbbreviated, DKDayOfMonthComplete, DKYearComplete, DKHour12hrComplete, DKMinutes2Digits, DKAmPm];
Which I personally think is a bit more readable like this:
NSString * dateFormatterString = dateFormatString(DKDayOfWeekAbbreviated, #", ", DKMonthNameAbbreviated, #" ", DKDayOfMonthComplete, #" ", DKYearComplete, #"'at' ", DKHour12hrComplete, #":", DKMinutes2Digits, #" ", DKAmPm, nil);
Question
I would prefer a way to iterate through the variables without having to pass nil into the function. Is there another way to iterate through a variable argument list, other than:
for (NSString * arg = string1; arg != nil; arg = va_arg(args, NSString*)) {
[formatString appendString:arg];
}

I assume you meant to say dateFormatter.dateFormat = ..., since NSDateFormatter has no dateFormatString property.
I assume DKDayOfWeekAbbreviated is a string constant defined as #"E", and DKDayOfWeekFull is #"EEEE", and so on, based on UTS #35.
If that is so, here's a different approach. Define your constants like this:
#define DKDayOfWeekAbbreviated #"E"
#define DKDayOfWeekFull #"EEEE"
#define DKMonthNameFull #"MMMM"
#define DKDayOfMonthComplete #"dd"
Then use compile-time string concatenation to build your strings. The compiler merges two adjacent string constants. For example, "hello " "world" becomes "hello world", and #"hello " #"world" becomes #"hello world". In fact, you can omit the second and later # characters, so #"hello " "world" becomes #"hello world".
Thus:
dateFormatter.dateFormat = DKDayOfWeekFull ", " DKMonthNameFull " " DKDayOfMonthComplete;
You don't need a helper function or varargs.

The only way to support variable arguments using the standard C syntax is to do what you are doing.
But you have another option - use an NSArray.
Your function becomes:
NSString * dateFormatString(NSArray *strings) {
// Set up our Format String
NSMutableString * formatString = [NSMutableString string];
// Build Format string
for (NSString * arg in strings) {
[formatString appendString:arg];
}
return formatString;
}
or simply do:
NSString * dateFormatString(NSArray *strings) {
return [strings componentsJoinedByString:#""];
}
And you call it like this:
dateFormatter.dateFormatString = dateFormatString(#[ DKDayOfWeekFull, #", ", DKMonthNameFull, #" ", DKDayOfMonthComplete ]);
No need for nil using the modern NSArray syntax.

Related

What does [urlString componentsSeperatedByString: #"?"] [0]? mean in Swift?

I'm not entirely sure what
NSString * fileName = [self cachedFileNameForKey:[urlString componentsSeparatedByString:#"?"][0]];
Means from this code. I am primely writing in Swift so this notation is a bit confusing for me. What does the double [ ] notation mean? 3d array?
+ (AVPlayerItem *)localDownloadedVideoFromUrl:(NSURL *)url {
NSString * urlString = url.absoluteString;
NSString * fileName = [self cachedFileNameForKey:[urlString componentsSeparatedByString:#"?"][0]];
TWRDownloadManager * manager = [TWRDownloadManager sharedManager];
if ([manager fileExistsWithName:fileName]) {
AVPlayerItem * item = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:[manager localPathForFile:fileName]]];
return item;
}
return [NSNull null];
}
[] in objective-c is not only used to access arrays, but also used to call methods. Surprising, isn't it?
In general,
[xxx someMethod];
is equivalent to:
xxx.someMethod()
in swift.
So here:
[urlString componentsSeparatedByString:#"?"][0]
means
urlString.components(separatedBy: "?")[0] // "[0]" can also be replaced by ".first", which is safer.
This is then passed to the cachedFileNameForKey as a parameter.
In Swift
var urlString: String = "Know someone who can answer? Share a link to this" // Example string or url.absoluteString
let fileName = urlString.components(separatedBy: "?")
print(fileName)
let string1 = fileName[0]
let string2 = fileName[1]
Hope will helpful to you
Ahh so mistake on my part is that I didn't see what the method -cacheFileNameForKey: was doing.
+ (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key {
const char *str = key.UTF8String;
if (str == NULL) {
str = "";
}
unsigned char r[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, (CC_LONG)strlen(str), r);
NSString *filename = [NSString stringWithFormat:#"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%#",
r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
r[11], r[12], r[13], r[14], r[15], [key.pathExtension isEqualToString:#""] ? #"" : [NSString stringWithFormat:#".%#", key.pathExtension]];
return filename;
}
I guess its used for a function to generate a file name for a temporary movie file.

How to use value of a variable in ObjectiveC?

I just start to learn ObjectiveC and I have a problem.
I have a variable in a for loop , this variable change value at any iteration and I want get his value.
Here my problem the variable is "name":
for 1st iteration name='id'
for 2nd iteration name='city' ...
for (i = 0; i < count; i++) {
name = ivar_getName(ivars[i]);
encode = ivar_getTypeEncoding(ivars[i]);
type = decode_type_string(encode);
if (strncmp(encode, "#", 1) == 0)
{
printf(" '%s', ", [[obj name*]UTF8String]);
}
else
{
printf(" %s ", name);
}
}
My goal is that the variable is interpreted by its value. if in my iiteration variable name is set to "ID" I want to have: [obj name] => [id obj] (for the id of my instance) if in my iiteration variable name is set to "City" I want to have: [obj name] => [obj City] (for the city of my instance)
(sorry for my english)
I think you are badly confused about several things. First off, NSString objects do not begin with "#" as the first character of the string. Rather, the "#" character is used to differentiate between a C string literal (eg, "abc") and an Objective-C NSString literal (eg, #"abc"). Note that the "#" character is outside the quotes.
Second, if you have an object with a PROPERTY named "name" you may reference its value with either [myObj name] or myObj.name, and set it either with [myObj setName:newValue]; or with myObj.name = newValue;.
If you have an NSString object you wish to print for diagnostic purposes, you can print it using NSLog(#"Here is the string ->%#", theString);. The %# is a special Objective-C formatting code meaning "treat the next format parm as an object and invoke it's description method, then display that result".
(And if you're "just learning" Objective-C there's no reason to be mucking with ivar_getName and its ilk. Functions like that are very rarely needed.)
If you are printing an Objective-C string, use NSLog
NSLog(#" %# ", [obj name]);
To print a string in objective-c you can use NSLog. The call looks like
NSLog(#"%#",[obj name]);
Not sure I quite read your question right - but I think you want to dynamically fetch them.
See below for an example.
Dw.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface Frob : NSObject;
#property (strong) NSString * name, * bar, * fred;
#end
#implementation Frob
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
Frob * frob = [[Frob alloc] init];
frob.name = #"aName value";
frob.bar = #"aBar value";
frob.fred = #"aFred value";
const char *lst[] = { "name", "bar", "fred" };
for(int i = 0; i < sizeof(lst)/sizeof(char*); i++)
{
const char * nameStr = lst[i];
SEL s = sel_getUid(nameStr);
if (s) {
NSString * val = [frob performSelector:s];
NSLog(#"%s --> %#\n", nameStr, val);
printf("%s --> %s", nameStr, [val UTF8String]);
}
}
}
return 0;
}

why does my sqlite sql return no results?

The follow is my db function:
+(NSArray*)searchWithKey:(NSString*)_key{
NSMutableArray* tmpArray = [NSMutableArray array];
static Statement* stmt = nil;
char* sql = "select * from Bookmarks where BMUrl like '%?%'";
if (stmt == nil) {
stmt = [DBConnection statementWithQuery:sql];
[stmt retain];
}
[stmt bindString:_key forIndex:1];
while ([stmt step] == SQLITE_ROW) {
BookMark* tmpBM = [[BookMark alloc] initWithStatement:stmt];
NSLog(#"tmpBM = %#",tmpBM);
[tmpArray addObject:tmpBM];
[tmpBM release];
}
[stmt reset];
return tmpArray;}
The keyword of sql is "like" which I use.But there are no results that the sqlite return.Anyone could tell me why?
I change the sql into "select * from Bookmarks where BMUrl like '%h%'",there are some results which are returned.So , I guess the mistake is the function "bindString:forIndex",the code is
- (void)bindString:(NSString*)value forIndex:(int)index{
sqlite3_bind_text(stmt, index, [value UTF8String], -1, SQLITE_TRANSIENT);}
which is the correct sqlite3 api that i will use? thank u!
Bindings aren't interpolated like that. If you put a quotation mark in a string, as in '%?%', it will be interpreted as a literal question mark.
You should instead modify your input _key:
Escape any instances of % and _ with a \
Add %s at the beginning and end
This prepares it to be used with a LIKE operator.
You also need to modify your SQL so that the ? represents a standalone parameter: ... where BMUrl like ?.
Here's an example for how to escape special characters and add %s at the beginning and end of _key:
NSString *escapedKey = [_key stringByReplacingOccurencesOfString:#"%"
withString:#"\\%"];
escapedKey = [escapedKey stringByReplacingOccurencesOfString:#"_"
withString:#"\\_"];
NSString *keyForLike = [NSString stringWithFormat:#"%%%#%%", escapedKey];
[stmt bindString:keyForLike forIndex:1];

EXC_BAD_ACCESS memory issue when accessing instance variables

I'm having issues porting a Java project I worked on a while ago to Objective-C code - I get "Program received signal: "EXC_BAD_ACCESS"", on the first line in this code:
-(Point3D *) unit {
NSLog(#"%#%#", "X: ", x);
double length = [self length];
return [[Point3D alloc] initWithX:x/length andY:y/length andZ:z/length];
}
called from here:
-(id) initWithStart:(Point3D *)start andDirection:(Point3D *)dir {
if ( self = [super init] ) {
NSLog(#"%#%#", #"Direction:", [dir toString]);
printf("Trying to find unit length of direction...\n");
NSLog(#"%#", [[dir unit] toString]);
self.start = start;
self.direction = [dir unit];
}
return self;
}
Console output is:
2011-12-09 17:20:14.021 RayTracerProject[16607:407] Direction:(0,0,20)
Trying to find unit length of direction...
The toString method of the Point3D looks like this:
-(NSString *) toString {
NSNumber *xstring = [NSNumber numberWithDouble:self.x];
NSNumber *ystring = [NSNumber numberWithDouble:self.y];
NSNumber *zstring = [NSNumber numberWithDouble:self.z];
NSString * str = #"(";
str = [str stringByAppendingString:[xstring stringValue]];
str = [str stringByAppendingString:#","];
str = [str stringByAppendingString:[ystring stringValue]];
str = [str stringByAppendingString:#","];
str = [str stringByAppendingString:[zstring stringValue]];
str = [str stringByAppendingString:#")"];
return str;
}
So, from what I can see, my (Point3D *) dir is alive and well when I check what the value is using my [dir toString] call. But when I try to call [dir unit], it seems I no longer have the variables I did in the object, hence the EXC_BAD_ACCESS error.
What am I doing wrong here? I think it's something to do with the way I'm managing (or not) my memory usage, but I don't know what it is.
The NSLog in your method 'unit' should be:
NSLog(#"%# %g", #"X:", x);
Moving the space is optional, but just makes it clearer. The key issues were that you were missing the # before the "X: ", and you need to use %g rather than %# because from your other code x is a double rather than an NSObject. Equivalent—but simpler and thus better—would also be:
NSLog(#"X: %g", x);
Apple's documentation provides the definitive guide to string format specifiers and that and the surrounding documentation explain the use of format specifiers like %# (to reference an Objective C object) and %g (to reference a 64-bit floating-point number) when constructing an NSString.
It seems this line here:
NSLog(#"%#%#", "X: ", x);
should look more like this:
NSLog(#"%#%lf", #"X: ", self.x);
Note a few changes here:
From your other code, it seems you have a property named 'x'. self.x references that property. (You may or may not also have an instance variable named 'x')
Also from your other code, it seems your property 'x' is a double. So the placeholder for a double is %lf, not %#. %# is for NSObjects (and descendants)
NSString literals start with #. So #"X: " is an NSString literal. What you had is a C string.
EDIT
Well, I'm slow as usual. :) Ignore what I said and accept Duncan's answer, which was faster and now cleaner, actually.

How to check if NSString begins with a certain character

How do you check if an NSString begins with a certain character (the character *).
The * is an indicator for the type of the cell, so I need the contents of this NSString without the *, but need to know if the * exists.
You can use the -hasPrefix: method of NSString:
Objective-C:
NSString* output = nil;
if([string hasPrefix:#"*"]) {
output = [string substringFromIndex:1];
}
Swift:
var output:String?
if string.hasPrefix("*") {
output = string.substringFromIndex(string.startIndex.advancedBy(1))
}
You can use:
NSString *newString;
if ( [[myString characterAtIndex:0] isEqualToString:#"*"] ) {
newString = [myString substringFromIndex:1];
}
hasPrefix works especially well.
for example if you were looking for a http url in a NSString, you would use componentsSeparatedByString to create an NSArray and the iterate the array using hasPrefix to find the elements that begin with http.
NSArray *allStringsArray =
[myStringThatHasHttpUrls componentsSeparatedByString:#" "]
for (id myArrayElement in allStringsArray) {
NSString *theString = [myArrayElement description];
if ([theString hasPrefix:#"http"]) {
NSLog(#"The URL is %#", [myArrayElement description]);
}
}
hasPrefix returns a Boolean value that indicates whether a given string matches the beginning characters of the receiver.
- (BOOL)hasPrefix:(NSString *)aString,
parameter aString is a string that you are looking for
Return Value is YES if aString matches the beginning characters of the receiver, otherwise NO. Returns NO if aString is empty.
As a more general answer, try using the hasPrefix method. For example, the code below checks to see if a string begins with 10, which is the error code used to identify a certain problem.
NSString* myString = #"10:Username taken";
if([myString hasPrefix:#"10"]) {
//display more elegant error message
}
Use characterAtIndex:. If the first character is an asterisk, use substringFromIndex: to get the string sans '*'.
NSString *stringWithoutAsterisk(NSString *string) {
NSRange asterisk = [string rangeOfString:#"*"];
return asterisk.location == 0 ? [string substringFromIndex:1] : string;
}
Another approach to do it..
May it help someone...
if ([[temp substringToIndex:4] isEqualToString:#"http"]) {
//starts with http
}
This might help? :)
http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/instm/NSString/characterAtIndex:
Just search for the character at index 0 and compare it against the value you're looking for!
This nice little bit of code I found by chance, and I have yet to see it suggested on Stack. It only works if the characters you want to remove or alter exist, which is convenient in many scenarios. If the character/s does not exist, it won't alter your NSString:
NSString = [yourString stringByReplacingOccurrencesOfString:#"YOUR CHARACTERS YOU WANT TO REMOVE" withString:#"CAN either be EMPTY or WITH TEXT REPLACEMENT"];
This is how I use it:
//declare what to look for
NSString * suffixTorRemove = #"</p>";
NSString * prefixToRemove = #"<p>";
NSString * randomCharacter = #"</strong>";
NSString * moreRandom = #"<strong>";
NSString * makeAndSign = #"&amp;";
//I AM INSERTING A VALUE FROM A DATABASE AND HAVE ASSIGNED IT TO returnStr
returnStr = [returnStr stringByReplacingOccurrencesOfString:suffixTorRemove withString:#""];
returnStr = [returnStr stringByReplacingOccurrencesOfString:prefixToRemove withString:#""];
returnStr = [returnStr stringByReplacingOccurrencesOfString:randomCharacter withString:#""];
returnStr = [returnStr stringByReplacingOccurrencesOfString:moreRandom withString:#""];
returnStr = [returnStr stringByReplacingOccurrencesOfString:makeAndSign withString:#"&"];
//check the output
NSLog(#"returnStr IS NOW: %#", returnStr);
This one line is super easy to perform three actions in one:
Checks your string for the character/s you do not want
Can replaces them with whatever you like
Does not affect surrounding code
NSString* expectedString = nil;
if([givenString hasPrefix:#"*"])
{
expectedString = [givenString substringFromIndex:1];
}