How can I create a static NSString[] of static NSString constants? - objective-c

I have a collection of static NSString consts in one of my header files that I would like to utilize in a new static NSArray (or NSString[]) but am running into the following error
Initializer Element is not a compile-time constant
My strings and array are set up as follows:
static NSString * const SOMEVAL = #"val";
static NSString * const SOMEKEY = #"key";
static NSString *KEYLIST[] = { SOMEVAL, SOMEKEY };
Is it possible to get this static array to compile in this manner or will the previously declared NSStrings always show up as non-compile-time constants?

The compiler will never treat objects as "compile-time constants"; you will have to do one of two workarounds.
You can use preprocessor definitions for the strings:
#define KEY #"key"
#define VAL #"val"
static NSString * const key = KEY;
static NSString * const val = VAL;
static NSString * keyVal[] = { KEY, VAL };
Or you can initialize the array in a function or method which is guaranteed to be called before you need the array. The constructor function attribute is one option:
static NSString * keyVal[2];
__attribute__((constructor))
void setKeyVal(void)
{
keyVal[0] = key;
keyVal[1] = val;
}
The +initialize method of a closely-related class is another.

You shouldn't use static in a header file. In the header declare:
extern NSString * const SOMEVAL;
extern NSString * const SOMEKEY;
extern NSString *KEYLIST[];
and then in a source file define:
NSString * const SOMEVAL = #"val";
NSString * const SOMEKEY = #"key";
NSString *KEYLIST[] = { SOMEVAL, SOMEKEY };

If you're willing to rely on the compiler combining identical string constants (a pretty safe bet), you can do this instead:
#define SOMEVAL #"val"
#define SOMEKEY #"key"
static NSString *KEYLIST[] = { SOMEVAL, SOMEKEY };

Related

Convert or compare "Unmanaged<NSString> and NSString

I have a framework in Objective-C with this struct:
VehicleClass.h
struct VehicleStruct
{
__unsafe_unretained NSString *const CAR;
__unsafe_unretained NSString *const MOTORCYCLE;
__unsafe_unretained NSString *const TRUCK;
};
VehicleClass.m
#import "VehicleClass.h"
const struct VehicleStruct Vehicle = {
.CAR = #"CAR",
.MOTORCYCLE = #"MOTORCYCLE",
.TRUCK = #"TRUCK"
};
And in my Contansts.h
extern const struct VehicleStruct Vehicle;
Well, now I want get car value in my proyect using swift:
let carValueSwift = String(Vehicle.CAR)
In console:
"Unmanaged NSString(_value: CAR)"
How can I solve this? I need get this value like NSString, because if then I want to compare in my framework:
["CAR" isEqual: carValueSwift] Doesn´t work.
Thank you very much
With takeUnretainedValue() the Unmanaged<NSString> is converted
to NSString (without taking ownership), and this can simply be
cast to a Swift String:
let carValueSwift = Vehicle.CAR.takeUnretainedValue() as String
print(carValueSwift)
Well, I found solution:
let car: Unmanaged<NSString> = Vehicle.CAR
let carString = convertCfTypeToString(car)
Function:
func convertCfTypeToString(cfValue: Unmanaged<NSString>!) -> String?
{
let value = Unmanaged.fromOpaque(
cfValue.toOpaque()).takeUnretainedValue() as CFStringRef
if CFGetTypeID(value) == CFStringGetTypeID(){
return value as String
} else {
return nil
}
}
If anyone knows other way to avoid use convertCfTypeToString: method, please, let me know...

Creating NSString results in null

I am trying to initialize an NSString. I tried initWithString: but this becomes invalid because the NSString becomes an NSPlaceholderString. I switched to UTF8string, but it returns a null value.
code below:
- (void) test:(NSString*)thing {
id (*p_objc_msgSend)(id p, SEL sel, ...);
id (*p_objc_getClass)(const char *s);
Class class;
p_objc_getClass = objc_getClass;
p_objc_msgSend = objc_msgSend;
NSString *s;
class = (*p_objc_getClass)( "NSString");
//SEL init = #selector(initWithString:);
s = (*p_objc_msgSend)(class, #selector(alloc));
s = (*p_objc_msgSend)(s, #selector(initWithUTF8String:), "AAA");
NSLog(#"Value of %#", s);
}
NSString is a class cluster with multiple private subclasses. NSPlaceholderString is a subclass of NSString so you can continue to use that object as any other NSString.

objective-c constants.h static const

I googled a lot of similar questions, but none of them had a really good answer about what i needed.
Im wondering about the best way to write constants in some .h file.
my constants file is just a clear file with no imports, just clear .h file. I import it in prefix file.
when i use
static NSInteger someInteger = 1;
or
static BOOl someBool = YES;
The compiler compiles okay but gives me a warning that this variable is unused even though im using it multiple times in different classes.
But if i use
static NSString* someString = #"";
there are not any warnings.
also if i use
const NSInteger someInteger = 1;
Compiler compiles okay for a real device, but when running on a simulator it does not compile with an error duplicate symbols for architecture i386
also what is the difference between
const NSString* someString = #"";
const NSInteger someInteger = 1;
and
NSString* const someString = #"";
NSInteger const someInteger = 1;
I ended up using static const NSInteger someInteger =1;, but i wonder if this is a right option.
So really my question is: what words should i use to successfully create a constants.h file?
For all types (both primitive or otherwise) then you need to provide a single implementation of it somewhere:
constants.h:
extern const int someValue;
extern NSString * const someString;
constants.m:
const NSInteger someValue = 1;
NSString * const someString = #"Some string";
You never want to use static variables in header files as you will end up with multiple copies of the "constant" in every implementation file that includes that header (they may not upset the linker, and cause a link error, but they are there).

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);
}

Enumerations in Objective-C

I'm making the jump from Java to Objective-C. I'm wondering if there is a concept analogous to Java's enums, that supports implementations of methods. I realize Objective-C has plain old C enumerations, but those are really just ints.
I'm looking to prevent switch-yards -- Java enums would be perfect.
Objective-C is just C with some additional markup for objects, no new types have been added.
That means, no.
For mutual exclusive flags, Apple uses strings.
header.h
extern NSString * const kNSSomeFlag;
extern NSString * const kNSOtherFlag;
extern NSString * const kNSThirdFlag ;
code.m
NSString * const kNSSomeFlag = #"kNSSomeFlag";
NSString * const kNSOtherFlag = #"kNSOtherFlag";
NSString * const kNSThirdFlag = #"kNSThirdFlag";
…
void myFunction(NSString *flag)
{
if (flag == kNSSomeFlag) {
// the code
}
}
An example of this can be found in the NSDistributedNotificationCenter.