Enumerations in Objective-C - 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.

Related

Expressing enum with associated values in Objective-C

In Swift there are enums with associated values:
enum Coffee {
case Black
case BlackWithSugar(spoons: Int)
// ....
}
Clearly the goal here is to:
Have one case that expresses BlackWithSugar (not 100 cases, like BlackWith1Sugar, BlackWith2Sugar) and so on
But preserve additional info (number of spoons) inside the same structure
What's the closest / most elegant way to express the same in Objective-C?
Note: I saw this question, but 2 answers do not say how to express the same intent in Objective-C. I'm ok with using something other than enum, and using Swift is not an option.
While it is possible with a lot of work to accomplish something very similar to Swift's enums with associated types in Objective-C (see the blog post I linked in a comment above), it requires a bunch of hard-to-comprehend code.
I'd recommend taking a more natural Objective-C style approach. You'll lose out on some of the type safety Swift provides, but ObjC is naturally a less type-safe language.
For example:
// Create an options enum. Bridges into Swift as an OptionSet
typedef NS_OPTIONS(NSInteger, CoffeeOptions) {
CoffeeOptionsBlack = 0,
CoffeeOptionsWithSugar = 1 << 0,
CoffeeOptionsWithCream = 1 << 1
};
// Create a (non-extensible) string-typed "enum". Bridges into Swift as an enum-style struct with string "raw values"
typedef NSString *CoffeeQuantityKey NS_TYPED_EXTENSIBLE_ENUM;
static CoffeeQuantityKey const CoffeeQuantityKeySpoonsOfSugar = #"SpoonsOfSugar";
static CoffeeQuantityKey const CoffeeQuantityKeyTeaspoonsOfCream = #"TeaspoonsOfCream";
#interface CoffeeMachine : NSObject
- (void)makeCoffeeWithOptions:(CoffeeOptions)options quantities:(NSDictionary<CoffeeQuantityKey, NSNumber *> *)quantities;
#end
#implementation CoffeeMachine
- (void)makeCoffeeWithOptions:(CoffeeOptions)options quantities:(NSDictionary<CoffeeQuantityKey, NSNumber *> *)quantities
{
// Make coffee
if (options & CoffeeOptionsWithSugar) {
NSNumber *sugarAmount = quantities[CoffeeQuantityKeySpoonsOfSugar] ?: #1; // Default to 1 spoon
NSLog(#"Adding %# spoons of sugar", sugarAmount);
}
if (options & CoffeeOptionsWithCream) {
NSNumber *creamAmount = quantities[CoffeeQuantityKeyTeaspoonsOfCream] ?: #1; // Default to 1 teaspoon
NSLog(#"Adding %# teaspoons of cream", creamAmount);
}
}
#end
This also has the advantage of bridging relatively nicely into Swift:
let cm = CoffeeMachine()
cm.makeCoffee(options: [.withSugar, .withCream], quantities: [.spoonsOfSugar : 3])

String substitution with variable constants [duplicate]

This question already has answers here:
Objective C Macro append to string
(3 answers)
Closed 6 years ago.
In my project, I define my urls like such:
#define TERMSURL #"http://127.0.0.1:8000/terms/"
#define PRIVACYURL #"http://127.0.0.1:8000/privacy/"
...
Since the root url (http://127.0.0.1:8000/) is always the same, is there a way to set it as a constant, and then use string substitution for the remaining pieces?
For example, in the other files, I could do something like this:
NSString *devBaseUrl = #"http://127.0.0.1:8000/";
NSString *url1 = [NSString stringWithFormat:#"%#terms/", devBaseUrl];
Is there a way to do that for my current approach?
shared.h
#define TERMSURL #"http://127.0.0.1:8000/terms/"
#define PRIVACYURL #"http://127.0.0.1:8000/privacy/"
#define URL_BASE #"http://127.0.0.1:8000/"
yourClass.m
NSString * stringUrlBase = URL_BASE;
NSString *url1 = [NSString stringWithFormat:#"%#terms/", stringUrlBase];
Sure, you can do that. 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 can I create a static NSString[] of static NSString constants?

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

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).

What is the best way to define string constants in an objective-c protocol?

I have defined a protocol that all my plug-ins must implement. I would also like the plug-ins to all use certain strings, like MyPluginErrorDomain. With integers this is quite easily achieved in an enum, but I can't figure out how to do the same with strings. Normally, in classes I would define
extern NSString * const MyPluginErrorDomain;
in the .h file and in the .m file:
NSString * const MyPluginErrorDomain = #"MyPluginErrorDomain";
but that doesn't work very well in a protocol, because then each plug-in would have to provide its own implementation which defeats the purpose of having a constant.
I then tried
#define MYPLUGIN_ERROR_DOMAIN #"MyPluginErrorDomain"
but the implementing classes in the plug-in can't seem to see the #define. Who knows a good solution?
You can declare them in the header with the protocol (but outside the protocol interface itself), then define them in an implementation file for the protocol (obviously it wouldn't have an #implementation section - just your NSString definitions).
Or have a separate .h/.m pair that is just for the string constants (the protocol header can import the string constants header).
You keep the .h definition:
extern NSString * const MyPluginErrorDomain;
but put this part into a separate .m file that gets included in your framework:
NSString * const MyPluginErrorDomain = #"MyPluginErrorDomain";
So plug-ins can still implement the interface but when compiling they link or compile in your other .m file, so they will see the value of MyPluginErrorDomain.
In C++, I would declare them in a header like this:
const char * const MYPLUGIN_ERROR_DOMAIN = "MyPluginErrorDomain";
const char * const MYPLUGIN_FOO_DOMAIN = "MyPluginFooDomain";
Note that as the pointers are const, they will be local to the translation units the header is #included in, and so there will be no need to use extern to prevent multiple definition errors.
You should implement it as extern strings as in your example:
extern NSString * const MyPluginErrorDomain;
or provide extern functions which return static storage data. For example:
/* h */
extern NSString * MyPluginErrorDomain();
/* m */
NSString * MyPluginErrorDomain() {
static NSString * const s = #"MyPluginErrorDomain";
return s;
}
The reason is that strings and keys are often used and compared by pointer value or hash value, rather than true string comparison (isEqualToString:).
At the implementation level, there is a big difference between:
In code, that means that when the strings compared are defined in multiple binaries:
Say 'MyPluginErrorDomain' and 'key' have identical string values, but are defined in different binaries (i.e. on in the plugin host, one in the plugin).
/////// Pointer comparison (NSString)
BOOL a = [MyPluginErrorDomain isEqualToString:key];
BOOL b = MyPluginErrorDomain == key;
// c may be false because a may be true, in that they represent the same character sequence, but do not point to the same object
BOOL c = a == b;
/////// Hash use (NSString)
// This is true
BOOL d = [MyPluginErrorDomain hash] == [key hash];
// This is indicative if true
BOOL e = [MyPluginErrorDomain hash] == [someOtherStringKey hash];
// because
BOOL f = [MyPluginErrorDomain isEqualToString:someOtherStringKey];
// g may be false (though the hash code is 'generally' correct)
BOOL g = e == f;
It is therefore necessary to provide the keys in many cases. It may seem like a trivial point, but it is hard to diagnose some of the problems associated with the difference.
Hash codes and pointer comparisons are used throughout Foundation and other objc technologies in the internals of dictionary storage, key value coding... If your dictionary is going straight out to xml, that's one thing, but runtime use is another and there are a few caveats in the implementation and runtime details.