Constant built from another constant not working - ios

I am beginning to delve into iOS development, and am learning Objective C. My background is Java.
I'm attempting to create a simple console game, and have created the following constants:
#import <Foundation/Foundation.h>
static const NSString *ROCK = #"Rock";
static const NSString *PAPER = #"Paper";
static const NSString *SCISSORS = #"Scissors";
static const NSString *LIZZARD = #"Lizzard";
static const NSString *SPOCK = #"Spock";
static const NSArray *WEAPONS = #[SPOCK, ROCK, SCISSORS, LIZZARD, PAPER];
The trouble is that I get an error on the last line: Initializer element is not a compile-time constant.
I tried to figure out what this means -- the closest thing I could find was this question by Fred Collins, where he notes that "This happens because objects works [sic] at runtime." I'm still not entirely sure what the implication of this is -- how is this different from Java? (I can definitely do this in Java!)
Regardless, I need some way of initializing NSArray *WEAPONS, and I can't make the answers to Fred Collin's question work for me without adding another file. (He is using a class for his constants, where as my program is simple enough to be contained in the same file as the main method.)

One proper way to initialize the array is to do this:
static const NSString *ROCK = #"Rock";
static const NSString *PAPER = #"Paper";
static const NSString *SCISSORS = #"Scissors";
static const NSString *LIZZARD = #"Lizzard";
static const NSString *SPOCK = #"Spock";
static const NSArray *WEAPONS = nil;
+ (void)initialize {
WEAPONS = #[SPOCK, ROCK, SCISSORS, LIZZARD, PAPER];
}
The initialize class method is a special class method that will only be called once before any instance is ever created or before any method (class or instance) is ever called.

NSArrays are not allowed to be used like that and must be done inside a method. Most everything with the exception of the NSString is disallowed.
Try something like
static const NSString *ROCK = #"Rock";
static const NSString *PAPER = #"Paper";
static const NSString *SCISSORS = #"Scissors";
static const NSString *LIZZARD = #"Lizzard";
static const NSString *SPOCK = #"Spock";
static const NSArray *WEAPONS = nil;
- (void)init
{
WEAPONS = #[SPOCK, ROCK, SCISSORS, LIZZARD, PAPER];
}

The proper way is to do it in the init method of the object, or
static const NSArray *WEAPONS = #[#"SPOCK", #"ROCK", #"SCISSORS", #"LIZZARD", #"PAPER"];
but I would not recommend this one.

Related

Objective C Macro append to string

I think this is a very simple thing to do, but since I'm new to iOS development and objective C, I can't figure it out.
#define RESTFUL_PATH_PREFIX #"https://gogch.com/gch-restful";
#define LOGIN RESTFUL_PATH_PREFIX #"/login;
I want the result "https://gogch.com/gch-restful/login"
but I'm getting the result as "https://gogch.com/gch-restful"
other topics in stackoverflow mention only about adding a new string to the beginning of a string like,
#define DOMAIN "example.com"
#define SUBDOMAIN "test." DOMAIN
Remove the trailing semi-colon:
#define RESTFUL_PATH_PREFIX #"https://gogch.com/gch-restful";
^
and then string constants can be concatenated by the compiler:
#"first" #"second"
instead of:
#"first"; #"second"
It is much better practice to use constants instead of define macros.
static NSString *const YourPath = #"https://...";
And then you can concatenate your strings with NSString stringWithFormat: method.
Since I made this answer to a question marked as a dupe, I'll also answer it here
Sure, you can use defines OR you can use NSString constants. It's really a matter of preference ... I have however seen both a #define and an NSString const * const being used before. Defines are easier, and you're probably not going to save that much memory by having constants instead of individual immutable instances of NSString all over the place.
Some advice is to think about how you export the NSString constants. You'll probably want EXTERN_PRIVATE instead of EXTERN, but my sample code will allow all clients of your header to read the string constants you've declared therein.
What you can do:
Create a new .m/.c file with a header in Xcode
In the .m/.c file, declare and initialise your constants
Export the constant as necessary so other compilation units can access it
constants.h
#ifndef constants_h
#define constants_h
// Export the symbol to clients of the static object (library)
#define EXTERN extern __attribute__((visibility("default")))
// Export the symbol, but make it available only within the static object
#define EXTERN_PRIVATE extern __attribute__((visibility("hidden")))
// Make the class symbol available to clients
#define EXTERN_CLASS __attribute__((visibility("default")))
// Hide the class symbol from clients
#define EXTERN_CLASS_PRIVATE __attribute__((visibility("hidden")))
#define INLINE static inline
#import <Foundation/Foundation.h>
EXTERN NSString const * _Nonnull const devBaseUrl;
#endif /* constants_h */
constants.m
#include "constants.h"
NSString const * _Nonnull const devBaseUrl = #"http://127.0.0.1:8000/";
main.m
#import <Foundation/Foundation.h>
#import "constants.h"
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSLog(#"Constant value: %#", devBaseUrl);
// Prints: Constant value: http://127.0.0.1:8000/
}
return 0;
}

How to get an "extern const int" value by its name

I would like to get the int value of my extern const by its name.
For example in my .h file:
extern const int MY_INT_CONST;
In my .m file:
const int MY_INT_CONST = 0;
What I want:
- (void) method {
int i = [getMyConstantFromString:#"MY_INT_CONST"];
}
How can I do that?
I searched in RunTime api and I did not find anything.
There's no simple way to do this. Neither the language nor the runtime provide a facility for this.
It can be done using the API of the dynamic loader to look up a symbol's address by its name.
// Near top of file
#include <dlfcn.h>
// elsewhere
int* pointer = dlsym(RTLD_SELF, "MY_INT_CONST");
if (pointer)
{
int value = *pointer;
// use value...
}
Note, that's a C-style string that's passed to dlsym(). If you have an NSString, you can use -UTF8String to get a C-style string.
No need for [getMyConstantFromString:#"MY_INT_CONST"];
directly use as follows
- (void) method {
int i = MY_INT_CONST;
}

Global constants in a static library are nil at run time

I have a static library which contains some constants such as:
// constants.h
NSString* const KDefaultProtocol;
// constants.m
NSString* const kDefaultProtocol = #"https";
// OtherCode.m
NSString *s = kDefaultProtocol;
When I run an app which links to the library the value of kDefaultProtocol is nil. Why is that?
(constants.m is included in the library compile sources section).
// constants.h
NSString* const KDefaultProtocol;
This one is incorrect. You meant:
// constants.h
extern NSString* const KDefaultProtocol;
The reason you're not getting the warning you should is because you've used mismatched the case of the leading K (which raises the question of how OtherCode.m even compiles; I suspect this is not exactly the code in question).
Try it->
// constants.h
static NSString * const KDefaultProtocol = #"https";

How do I convert NSString to const void

I am currently having issues converting NSString to a const void *
Here is my code:
DoSomethingFunction:(const void *)parameter
[class DoSomeThingFunction:(const void *)passswordField.text]
Password field is a UITextfield. The value becomes null when I try and cast it.
I want passwordField.text to be a const void * so it can be used in the function.
It depends on function implementation. It can be like this:
NSString *string = #"text";
const void *parameter = CFBridgingRetain(string);
DoSomethingFunction(parameter);
If function has similar parameter handling
void DoSomethingFunction(const void *parameter) {
NSString *string = CFBridgingRelease(parameter);
NSLog(#"%#", string);
}
Try casting the result of one of the NSString's methods that return a C string:
– cStringUsingEncoding:
– getCString:maxLength:encoding:
– UTF8String
NSString Class Reference

Best way to enum NSString

Im digging for ways to enum objc object such as NSString, I remember there a new feature in a version of Xcode4+ which offering a new way to enum , but not clearly. Anyone know that?
OK, I answered myself. Guess I make a mistake.
This is the new feature I mentioned above:
typedef enum Language : NSUInteger{
ObjectiveC,
Java,
Ruby,
Python,
Erlang
}Language;
It's just a new syntax for enum in Xcode 4.4, but I'm so foolish to think we can exchange "NSUInteger" to "NSString".
So here is the way I found that works:
http://longweekendmobile.com/2010/12/01/not-so-nasty-enums-in-objective-c/
// Place this in your .h file, outside the #interface block
typedef enum {
JPG,
PNG,
GIF,
PVR
} kImageType;
#define kImageTypeArray #"JPEG", #"PNG", #"GIF", #"PowerVR", nil
...
// Place this in the .m file, inside the #implementation block
// A method to convert an enum to string
-(NSString*) imageTypeEnumToString:(kImageType)enumVal
{
NSArray *imageTypeArray = [[NSArray alloc] initWithObjects:kImageTypeArray];
return [imageTypeArray objectAtIndex:enumVal];
}
// A method to retrieve the int value from the NSArray of NSStrings
-(kImageType) imageTypeStringToEnum:(NSString*)strVal
{
NSArray *imageTypeArray = [[NSArray alloc] initWithObjects:kImageTypeArray];
NSUInteger n = [imageTypeArray indexOfObject:strVal];
if(n < 1) n = JPG;
return (kImageType) n;
}
FYI. The original author of the second example code created a category for enum handling. Just the thing for adding to your very own NSArray class definition.
#interface NSArray (EnumExtensions)
- (NSString*) stringWithEnum: (NSUInteger) enumVal;
- (NSUInteger) enumFromString: (NSString*) strVal default: (NSUInteger) def;
- (NSUInteger) enumFromString: (NSString*) strVal;
#end
#implementation NSArray (EnumExtensions)
- (NSString*) stringWithEnum: (NSUInteger) enumVal
{
return [self objectAtIndex:enumVal];
}
- (NSUInteger) enumFromString: (NSString*) strVal default: (NSUInteger) def
{
NSUInteger n = [self indexOfObject:strVal];
if(n == NSNotFound) n = def;
return n;
}
- (NSUInteger) enumFromString: (NSString*) strVal
{
return [self enumFromString:strVal default:0];
}
#end
Alternative way to use struct:
extern const struct AMPlayerStateReadable
{
__unsafe_unretained NSString *ready;
__unsafe_unretained NSString *completed;
__unsafe_unretained NSString *playing;
__unsafe_unretained NSString *paused;
__unsafe_unretained NSString *broken;
} AMPlayerState;
const struct AMPlayerStateReadable AMPlayerState =
{
.ready = #"READY",
.completed = #"COMPLETE",
.playing = #"PLAYING",
.paused = #"PAUSED",
.broken = #"BROKEN"
};
Then you can use like this:
NSString *status = AMPlayerState.ready;
Easy to use, readable.
Would be nice if someone update/edit answer with advantages/disadvantages of this approach.
Recommended way from apple docs:
You use the NS_TYPED_ENUM to group constants with a raw value type that you specify. Use NS_TYPED_ENUM for sets of constants that can't logically have values added in a Swift extension, and use NS_TYPED_EXTENSIBLE_ENUM for sets of constants that can be expanded in an extension.
Apple docs
typedef NSString *MyEnum NS_TYPED_ENUM;
extern MyEnum const MyEnumFirstValue;
extern MyEnum const MyEnumSecondValue;
extern MyEnum const MyEnumThirdValue;
in the .h file. Define your strings in the .m file
MyEnum const MyEnumFirstValue = #"MyEnumFirstValue"
MyEnum const MyEnumSecondValue = #"MyEnumSecondValue";
MyEnum const MyEnumThirdValue = #"MyEnumThirdValue";
Works as expected in both Objective-C
- (void)methodWithMyEnum:(MyEnum)myEnum { }
and Swift
func method(_ myEnum: MyEnum) { }
This will be validated by compiler, so you won't mix up indices accidentally.
NSDictionary *stateStrings =
#{
#(MCSessionStateNotConnected) : #"MCSessionStateNotConnected",
#(MCSessionStateConnecting) : #"MCSessionStateConnecting",
#(MCSessionStateConnected) : #"MCSessionStateConnected",
};
NSString *stateString = [stateStrings objectForKey:#(state)];
<nbsp;>
var stateStrings: [MCSessionState: String] = [
MCSessionState.NotConnected : "MCSessionState.NotConnected",
MCSessionState.Connecting : "MCSessionState.Connecting",
MCSessionState.Connected : "MCSessionState.Connected"
]
var stateString = stateStrings[MCSessionState.Connected]
UPDATE: A more Swifty way is to extend the enum with CustomStringConvertible conformance. Also, this way the compiler will safeguard to implement every new addition to the underlying enum (whereas using arrays does not), as switch statements must be exhaustive.
extension MCSessionState: CustomStringConvertible {
public var description: String {
switch self {
case .notConnected:
return "MCSessionState.notConnected"
case .connecting:
return "MCSessionState.connecting"
case .connected:
return "MCSessionState.connected"
#unknown default:
return "Unknown"
}
}
}
// You can use it like this.
var stateString = MCSessionState.connected.description
// Or this.
var stateString = "\(MCSessionState.connected)"
Update in 2017
Recent down votes drew my attention, and I'd like to add that enum is really easy to work with String now:
enum HTTPMethod: String {
case GET, POST, PUT
}
HTTPMethod.GET.rawValue == "GET" // it's true
Original Answer
Unfortunately I ended up using:
#define HLCSRestMethodGet #"GET"
#define HLCSRestMethodPost #"POST"
#define HLCSRestMethodPut #"PUT"
#define HLCSRestMethodDelete #"DELETE"
typedef NSString* HLCSRestMethod;
I know this is not what OP asked, but writing actual code to implement enum seems to be an overkill to me. I would consider enum as a language feature (from C) and if I have to write code, I would come up with some better classes that does more than enum does.
Update
Swift version seems to be prettier, although the performance can never be as good.
struct LRest {
enum HTTPMethod: String {
case Get = "GET"
case Put = "PUT"
case Post = "POST"
case Delete = "DELETE"
}
struct method {
static let get = HTTPMethod.Get
static let put = HTTPMethod.Put
static let post = HTTPMethod.Post
static let delete = HTTPMethod.Delete
}
}
I think you are looking for the inline array function. eg
#[#"stringone",#"stringtwo",#"stringthree"];
if not, i'm not sure you can enum objects.
you could however have a static array of strings and have the enum reference object at index.
This is how I do it, although it's not perfect. I feel the switch mechanism could be improved... also not positive about hash-collision resistance, don't know what apple uses under the hood.
#define ElementProperty NSString *
#define __ElementPropertiesList #[#"backgroundColor", #"scale", #"alpha"]
#define epBackgroundColor __ElementPropertiesList[0]
#define epScale __ElementPropertiesList[1]
#define epAlpha __ElementPropertiesList[2]
#define switchElementProperty(__ep) switch(__ep.hash)
#define caseElementProperty(__ep) case(__ep.hash)
-(void)setValue:(id)value forElementProperty:(ElementProperty)ep;
[self setValue:#(1.5) forElementProperty:epScale];
//Compiler unfortunately won't warn you if you are missing a case
switchElementProperty(myProperty) {
caseElementProperty(epBackgroundColor):
NSLog(#"bg");
break;
caseElementProperty(epScale):
NSLog(#"s");
break;
caseElementProperty(epAlpha):
NSLog(#"a");
break;
}

Resources