how to do string enums and such in objective c - objective-c

I have filenames and such that are NSStrings. I have to keep typing them in and I may mistype and cause an error. How does the experienced objective C programmer handle this so they only type it in once?

In the header:
extern NSString * const kMyFile;
in the implementation file
NSString * const kMyFile = #"FileName.txt";
You get code completion on it this way.
Or, if it is only being used in one class and doesn't need to be exposed in the header you could just do
static NSString * const kMyFile = #"FileName.txt";

- (void)viewDidLoad {
//assign names here
//create variable names in the header--> NSArray *filename;
filename = [[NSArray alloc] initWithObjects: #"File1", #"File2", nil];
//[filename objectAtIndex: i] --> NSString
}

Related

How to access constant string in runtime using a string which holding its name?

I have some constant strings defined in my #implementation file like:
static NSString * const contentDisplayDateKeyPath = #"content.display_date";
static NSString * const contentIDKeyPath = #"content.id";
Could I get the content of contentDisplayDateKeyPath use a string which holding the variable's name in runtime?
ex:
NSString *constantName = #"contentDisplayDateKeyPath"
[self valueForKey:constantName]
then I'll get content.display_date
Can this be achieved?
I am trying to achieve this by using CFBundleGetDataPointer
CFBundleRef mainBundle = CFBundleGetBundleWithIdentifier(CFBridgingRetain([[NSBundle mainBundle] bundleIdentifier]));
void *stringPointer = CFBundleGetDataPointerForName(mainBundle, CFBridgingRetain(obj));
NSString *string = (__bridge NSString *)stringPointer;
But the stringPointer is always null.
Thanks for help
This should do it for you.
NSString *__autoreleasing *string = (NSString*__autoreleasing*)dlsym(RTLD_DEFAULT, "<name of constant like PKPaymentNetworkVisa>");
NSLog(#"%#", *string);
Use a map with the key as the constant name and the value as the constant value:
static NSDictionary *_constants = #{
#"contentDisplayDateKeyPath" : #"content.display_date",
#"contentIDKeyPath" : #"content.id",
// etc.
};
...
NSString *constantName = #"contentDisplayDateKeyPath";
NSString *constantValue = _constants[constantName];
Another option is to encapsulate this into a singleton object and access your constants through read only properties. Check out What should my Objective-C singleton look like? to see the singleton design pattern.
This question was answered here:
How do I lookup a string constant at runtime in Objective-C?
The solution worked perfectly for me.
You can use CFBundleGetDataPointerForName to lookup a constant's value at runtime
-(NSString *)lookupStringConstant:(NSString *)constantName
{
void ** dataPtr = CFBundleGetDataPointerForName(CFBundleGetMainBundle(), (__bridge CFStringRef)constantName);
return (__bridge NSString *)(dataPtr ? *dataPtr : nil);
}

Make variable names variables (Objective-c)

I'd like to do something like this:
.h
LocalRoom* zone1;
LocalRoom* zone2;
LocalRoom* zone3;
LocalRoom* zone4;
etc....
.m
NSString *number = 1;
NSString *variable = [NSString stringWithFormat: #"zone%#", number]
[[variable variableValue] broadcastChatMessage:redStringPrefix fromUser:#"server"];
emulating:
[zone1 broadcastChatMessage:redStringPrefix fromUser:#"server"];
How do I do this? Is it possible?
Given names like “zone1”, “zone2”, etc., I would make an array rather than a dictionary. Either way, these should not be separate variables.
#property(strong) NSMutableDictionary *zones;
-(id)init {
....
_zones = [[NSMutableDictionary alloc] init];
....
}
aZone = [_zones objectForKey: [NSString stringWithFormat:#"zone%d", someZoneNumber]];
... etc ...
You can do something similar using Key-Value Coding. You would write
[[self valueForKey:variable] broadcastChatMessage:redStringPrefix fromUser:#"server"];
rather than
[[variable variableValue] broadcastChatMessage:redStringPrefix fromUser:#"server"];
This would of course require that zone1, zone2, etc. are instance variables (or properties) on self.

Is there a way to allocate an Objective-C NSArray with objects contained in a C-style macro?

What I'd like to do is something like this:
NSArray *someArray = [[NSArray alloc] initWithObjects: C_MACRO, nil];
With the C_MACRO part being an outlying file that uses a #define to list some number of NSStrings, so when I need to change the string objects that populate the array I can conveniently do in one file. So far, it seems this is impossible. But then again, I'm a novice.
Anyone care to enlighten me?
No problem doing this... A macro is just text-replacement.
/* foo.h */
#define C_MACRO #"foo", #"bar"
/* bar.m */
#import "foo.h"
NSArray * someArray = [ [ NSArray alloc ] initWithObjects: C_MACRO, nil ];
Note you can use macros inside another macro. So
#define FOO_STR #"foo"
#define BAR_STR #"bar"
#define STR_LIST FOO_STR, BAR_STR
Another way is to have your strings allocated in a .m file, and declared as extern in a public header file. Handy if unique instances of the same object has to be shared.
/* foo.h */
extern NSString * const fooStr;
extern NSString * const barStr;
/* foo.m */
NSString * const fooStr = #"foo";
NSString * const BarStr = #"bar";
Why does it have to be a macro? The following works just as well:
NSString *words[] = {#"Hello", #"World!"}; // this can be a global
...
NSArray *array = [NSArray arrayWithObjects:words count:sizeof(words)/sizeof(*words)];

Is there a concise way to map a string to an enum in Objective-C?

I have a string I want to parse and return an equivalent enum. I need to use the enum type elsewhere, and I think I like how I'm defining it. The problem is that I don't know a good way to check the string against the enum values without being redundant about the order of the enums.
Is there no option other than a big if/else?
typedef enum {
ZZColorRed,
ZZColorGreen,
ZZColorBlue,
} ZZColorType;
- (ZZColorType)parseColor:(NSString *)inputString {
// inputString will be #"red", #"green", or #"blue" (trust me)
// how can I turn that into ZZColorRed, etc. without
// redefining their order like this?
NSArray *colors = [NSArray arrayWithObjects:#"red", #"green", #"blue", nil];
return [colors indexOfObject:inputString];
}
In Python, I'd probably do something like the following, although to be honest I'm not in love with that either.
## maps url text -> constant string
RED_CONSTANT = 1
BLUE_CONSTANT = 2
GREEN_CONSTANT = 3
TYPES = {
'red': RED_CONSTANT,
'green': GREEN_CONSTANT,
'blue': BLUE_CONSTANT,
}
def parseColor(inputString):
return TYPES.get(inputString)
ps. I know there are color constants in Cocoa, this is just an example.
try this: Map enum to char array
Pseudo code.. untested.
int lookup(const char* str) {
for(name = one; name < NUMBER_OF_INPUTS; name++) {
if(strcmp(str, stats[name]) == 0) return name;
}
return -1;
}
A more objective-c'ish version of the code could be:
// build dictionary
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
for(i=0; i<number_of_strings; i++) {
[dict setObject:[NSNumber numberWithInteger:i] forKey:[NSString stringWithUTF8String:names[i]]];
}
// elsewhere... lookup in dictionary
id obj = [dict objectForKey:name];
if(obj) return [obj intValue];
return -1;
This has already been answered: Converting between C enum and XML
Basically, you wind up defining corresponding strings when you define your enum, and then you use a category on NSArray so that you can do this:
static NSArray* colorNamesArray = [[NSArray alloc] initWithObjects:colorNames];
//colorNames is a nil-terminated list of string literals #defined near your enum
NSString* colorName = [colorNamesArray stringWithEnum:color];
//stringWithEnum: is defined with a category
Sure, the #define is a little ugly, but the code above, which is what you'll work with most of the time, is actually pretty clean.
I was never satisfied with any of the suggestions. (But I appreciate the effort that went into them.) I tried a few of them but they didn't feel good or were error-prone in practice.
I ended up created a custom dictionary to map integers to strings which feels a lot better because it's Cocoa through and through. (I didn't subclass NSDictionary in order to make it harder to misuse.)
#interface ZZEnumDictionary : NSObject {
NSMutableDictionary *dictionary;
}
+ (id)dictionary;
+ (id)dictionaryWithStrings:(id)firstString, ...;
- (NSString *)stringForInt:(NSInteger)intEnum;
- (NSInteger)intForString:(NSString *)stringEnum;
- (BOOL)isValidInt:(NSInteger)intEnum;
- (BOOL)isValidString:(NSString *)stringEnum;
- (BOOL)stringEquals:(NSString *)stringEnum intEnum:(NSInteger)intEnum;
- (BOOL)setContainsString:(NSSet *)set forInt:(NSInteger)intEnum;
- (NSArray *)allStrings;
#end
#interface ZZEnumDictionary ()
- (void)setInt:(NSInteger)integer forString:(NSString *)string;
#end

Creating object named after the value of an NSString - is it possible?

Is it possible to create an object named after an NSString's value? If so, how?
Try this:
Class theClass = NSClassFromString(someString);
id object = [[theClass alloc] init];
If you mean that the string specifies the class name, then yes it's easy to do this using the NSClassFromString function to lookup the appropriate class "factory" object:
NSString* myClassName = #"NSNumber";
id myNewObject = [[NSClassFromString(myClassName) alloc] init];
// myNewObject is an NSNumber...
The example is contrived, but you get the idea.
Yes:
//In your header
extern NSString *FrobnitzerCalibrationHigh;
extern NSString *FrobnitzerCalibrationMedium;
extern NSString *FrobnitzerCalibrationLow;
//In your implementation
NSString *FrobnitzerCalibrationHigh = #"FrobnitzerCalibrationHigh";
NSString *FrobnitzerCalibrationMedium = #"FrobnitzerCalibrationMedium";
NSString *FrobnitzerCalibrationLow = #"FrobnitzerCalibrationLow";
You can make a couple of macros and put them in your prefix header:
//Semicolons intentionally omitted (see below)
#define DECLARE_STRING_CONSTANT(name) extern NSString *name
#define DEFINE_STRING_CONSTANT(name) NSString *name = ##name
Then use them in your class headers and implementations:
//In your header
DECLARE_STRING_CONSTANT(FrobnitzerCalibrationHigh);
DECLARE_STRING_CONSTANT(FrobnitzerCalibrationMedium);
DECLARE_STRING_CONSTANT(FrobnitzerCalibrationLow);
//In your implementation
DEFINE_STRING_CONSTANT(FrobnitzerCalibrationHigh);
DEFINE_STRING_CONSTANT(FrobnitzerCalibrationMedium);
DEFINE_STRING_CONSTANT(FrobnitzerCalibrationLow);
(The macros omit the semicolons because their usages will supply them. If the macros had semicolons as well, the expansion would be extern NSString *FrobnitzerCalibrationHigh;;—harmless in this case, but it would bug me if I did this, largely because it's not harmless in some other cases.)