Objective C provides several ways to declare an enumeration. It could be declared via typedef enum or NS_ENUM. NS_ENUM macro takes type name as a parameter, and I do not completely understand its meaning. I didn't find description of NS_ENUM macro in official Apple documentation. What's a difference between using enum and NS_ENUM? And an other question if it's possible to use any other type in NS_ENUM instead NSInteger and its relative integer types?
NSHipster provided a very nice post that explains this thoroughly:
http://nshipster.com/ns_enum-ns_options/
To quote the bottom line:
This approach combines the best of all of the aforementioned approaches (enum, typedef enum), and even provides hints to the compiler for type-checking and switch statement completeness.
The main difference is that typedef NS_ENUM translates to a Swift enum properly, whereas typedef enum doesn't.
Here is the link to the official Apple's documentation:
https://developer.apple.com/library/content/releasenotes/ObjectiveC/ModernizationObjC/AdoptingModernObjective-C/AdoptingModernObjective-C.html#//apple_ref/doc/uid/TP40014150-CH1-SW6
From the subsection titled "Enumeration Macros":
The NS_ENUM and NS_OPTIONS macros provide a concise, simple way of defining enumerations and options in C-based languages. These macros improve code completion in Xcode and explicitly specify the type and size of your enumerations and options. Additionally, this syntax declares enums in a way that is evaluated correctly by older compilers, and by newer ones that can interpret the underlying type information.
Use the NS_ENUM macro to define enumerations, a set of values that are mutually exclusive:
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
The NS_ENUM macro helps define both the name and type of the enumeration, in this case named UITableViewCellStyle of type NSInteger. The type for enumerations should be NSInteger.
Related
So I been used to use this format to declare a enum types:
typedef enum SortType {
SORT_BY_NAME,
SORT_BY_COMPANY,
SORT_BY_NONE
} SortType;
But I saw some people declare it this way
typedef enum {
SORT_BY_NAME,
SORT_BY_COMPANY,
SORT_BY_NONE
} SortType;
Both seems to work and no error. But I want to know which is correct.
I would recommend:
typedef NS_ENUM(NSInteger, SortType) {
SortTypeName,
SortTypeCompany,
SortTypeNone
};
as per the Apple Developer Guides and Sample Code: Adopting Modern Objective-C > Enumeration Macros
Between those two, there isn't a wrong form per se. That said, the current recommended way to declare enums in Objective-C is using the NS_ENUM macro:
typedef NS_ENUM(NSInteger, SortType) {
SORT_BY_NAME,
SORT_BY_COMPANY,
SORT_BY_NONE
};
From Apple's Adopting Modern Objective-C guide:
The NS_ENUM and NS_OPTIONS macros provide a concise, simple way of defining enumerations and options in C-based languages. These macros improve code completion in Xcode and explicitly specify the type and size of your enumerations and options. Additionally, this syntax declares enums in a way that is evaluated correctly by older compilers, and by newer ones that can interpret the underlying type information.
Use the NS_ENUM macro to define enumerations, a set of values that are mutually exclusive:
The NS_ENUM macro helps define both the name and type of the enumeration, in this case named UITableViewCellStyle of type NSInteger. The type for enumerations should be NSInteger.
I have this NS_ENUM called ObserveType having two properties called Observe and ObserveAll. I can access the ObserveAll property as you can see from the picture, but I can't access Observe.
The NS_ENUM is in a header file of objective-C.
I know that changing Observe to ObserveX or ObserveXYZ will work.
But how do I access Observe without changing the name of the Observe?
Notice that I have to access the Observe on Swift.
In addition to answers above, I'd like to point out that you can give your Objective-C NS_ENUM a Swift name with NS_SWIFT_NAME macro:
typedef NS_ENUM(NSUInteger, XYZAwesomeEnum) {
XYZAwesomeEnumA,
XYZAwesomeEnumB,
XYZAwesomeEnumC,
} NS_SWIFT_NAME(AwesomeEnum);
Use it later in Swift:
AwesomeEnum.a
Apple Developer: Renaming Objective-C APIs for Swift
From Language Guide - Interoperability - Interacting with C APIs:
"The prefixes to C enumeration case names are removed when they are imported into Swift, whether they’re defined in system frameworks or
in custom code."
This means your first case in the ObserveType enum have no name after being imported to Swift (which I'm somewhat surprised doesn't yield a compile error). If we were to print out the conversion, it would look something like (conceptually)
typedef NS_ENUM(NSInteger, ObserveType) {
Observe = 0
ObserveAll = 1
};
// imported like ...
enum ObserveType: Int {
case _ = 0 // obviously illegal if applied directly in Swift
case All
}
You could try to access the nameless case by using its rawValue (0), but I would recommend updating the name of the first case in your Obj-C enum, if possible.
if let empty = ObserveType(rawValue: 0) {
print(empty) // prints ""?
}
dfri's answer is spot on, and to extend on his answer, it is probably best to name the enum and its cases following the widely adopted conventions. In the following code, I am making certain guesses on your intention.
typedef NS_ENUM(NSUInteger, XYZObserveType) {
XYZObserveOne,
XYZObserveAll
}
Another tip that I find useful when working with projects contain both Swift and Objective-C code is Generated Interface in the source editor.
When you are viewing an Objective-C class, selecting this option will show you the generated Swift header, which can be helpful in many occasions.
On the Adopting Modern Objective-C guide, Apple recommends using the NS_ENUM macro instead of enum. I've also read an explanation from NSHipster about NS_ENUM and NS_OPTIONS.
Maybe I've missed something but I don't quite understand what is the difference between the following two snippets and if any why is NS_ENUM the recommended way to go (except maybe, for backwards compatibility with older compilers)
// typedef enum
typedef enum {
SizeWidth,
SizeHeight
}Size;
// typedef NS_ENUM
typedef NS_ENUM(NSInteger, Size) {
SizeWidth,
SizeHeight
};
First, NS_ENUM uses a new feature of the C language where you can specify the underlying type for an enum. In this case, the underlying type for the enum is NSInteger (in plain C it would be whatever the compiler decides, char, short, or even a 24 bit integer if the compiler feels like it).
Second, the compiler specifically recognises the NS_ENUM macro, so it knows that you have an enum with values that shouldn't be combined like flags, the debugger knows what's going on, and the enum can be translated to Swift automatically.
NS_ENUM allows you to define a type. This means that the compiler can check if you're assigning the enum to a different variable like so:
//OK in both cases
NSInteger integer = SizeWidth;
//OK only with typedef
BOOL value = SizeHeight;
NS_ENUM also provides checks in switch statements that you've covered all possible values:
//Will generate warning if using NS_ENUM
switch(sizeVariable) {
case SizeWidth:
//Do something
}
I was looking through the NSString header file to see how Apple writes their enumerations and came across this piece of code:
enum {
NSStringEncodingConversionAllowLossy = 1,
NSStringEncodingConversionExternalRepresentation = 2
};
typedef NSUInteger NSStringEncodingConversionOptions;
This leaves me with a couple questions.
Why have they used anonymous enumerations? Is this method advantageous?
Is the typedef NSUInteger NSStringEncodingConversionOptions; line a good idea to include normally, or is it only used here because they have declared an anonymous enumeration?
That strange-looking definition is there to clearly define the bit-width and the signed-ness of an enum in a code both in 64-bit and 32-bit environment. It's detailed in this Apple document, but let me write down the summary here.
Apple in the past used the standard typedef enums before, as in
typedef enum { .... } NSEnumTypeName;
before 64bit-32bit universal binaries were (re)introduced. (I used "re" because the FAT binaries have been there since the NeXTStep days. Anyway.)
However, this makes the bit-width and the signed-ness of the typedef'd type NSEnumTypeName to be implementation-defined as specified in the Official Standard, see 6.7.2.2.4.
That makes it even trickier to write a code which can be compiled with various compilers and with various bit-width.
So Apple switched from the standard enum to the anonymous enum with a corresponding typedef to a specific signed/unsigned integer type.
At the CocoaHeads Öresund meeting yesterday, peylow had constructed a great ObjC quiz. The competition was intense and three people were left with the same score when the final question was to be evaluated: How many reserved keywords does Objective-C add to C?
Some spirited debate followed. All agreed that #interface, #implementation etc are all pre-processor directives rather than keywords, but how about something like in? It might be a keyword, but it's not a reserved keyword. For example, the following will compile without errors or warnings:
NSArray* in;
for (in in in)
NSLog(#"bwahahaa");
We concluded that ObjC adds no reserved keywords to C, and someone won a seemingly well-earned book.
But today I tried some more systematic abuse on the compiler by trying things like this:
int self = 45;
self++;
int y = self;
That compiles fine, and the same code works replacing self for BOOL, bycopy, inout, oneway, byref, SEL, and IMP.
Using id as the variable name, the first and last lines compile, but not the second one. The same goes for Protocol, and Class.
Using super, the first line compiles, but not the second and third.
With YES, NO, and NULL, all three lines fail to compile, probably because they are just defined as true, false, and nil.
It looks to me like a lot of this is gcc getting confused and I'm not so sure it reflects what is and isn't a reserved keyword in Objective-C. Why, for example, is it ok to use self as the name of an int, but not super?
The fact that the first assignment always works (except for with YES, NO, and NULL) would seem to support the idea that none of the candidates are technically reserved keywords that are not found in C. Or?
Could someone please give us an authoritative explication of this thorny issue?
Several people's honor is at stake.
EDIT: As Nikolai Ruhe pointed out, we need a clear definition of "keyword" to proceed. Niko cited a Wikipedia article saying that a keyword is "a word or identifier that has a particular meaning".
I think it is reasonable to use this definition from the same article:
In many languages, such as C and
similar environments like C++, a
keyword is a reserved word which
identifies a syntactic form. Words
used in control flow constructs, such
as if, then, and else are keywords. In
these languages, keywords cannot also
be used as the names of variables or
functions.
Furthermore, as the article states:
Typically, when a programmer attempts
to use a keyword for a variable or
function name, a compilation error
will be triggered.
In this sense, then, are there any reserved keywords that are predefined in the language’s formal specifications and cannot be used as a user-defined name?
All agreed that #interface, #implementation etc are all pre-processor directives rather than keywords
Then all were mistaken. #import and #pragma are preprocessor directives.
#interface, #implementation, #protocol and so forth are keywords of Objective-C, and they are compiler directives. They haven't been preprocessor directives since NeXT extended GCC to compile Objective-C without a Stepstone's original Objective-C pre-processor implementation.
Re: Nikolai Ruhe's suggest that self is a keyword
self is a parameter to an Objective-C method of type id (similar to how _cmd is also a param, of type SEL) and not a keyword. nil is a macro which expands to NULL. I'm a little disappointed that super doesn't expand to a macro, nor is it a parameter.
From the wikipedia entry:
Objective-C is a thin layer on top of C, and moreover is a strict superset of C. It is possible to compile any C program with an Objective-C compiler, and to freely include C code within an Objective-C class.
That would preclude Objective-C from adding any restricted keywords to the language.
Wikipedia defines keywords as:
In computer programming, a keyword is a word or identifier that has a particular meaning to the programming language.
I'd translate that to: a keyword in a language is a non-terminal in its grammar. (Btw, the grammar doesn't contain the word "keyword" just by itself, so no confusion there).
The terminals in the grammar would be:
void
char
short
int
long
float
double
signed
unsigned
id
const
volatile
in
out
inout
bycopy
byref
oneway
self
super
#interface
#end
#implementation
#end
#interface
#end
#implementation
#end
#protoco
#end
#class
Grammar in Appendix B of the original 1995 book defines super as "literal symbol" terminal and it's a special-case in message receiver syntax.
in literal symbol is part of protocol-qualifier in ObjC 1.0 and was reused in fast enumeration syntax in ObjC 2.0 (which has no formal grammar definition AFAIK).
id is defined as part of type-specifier alongside void, char, unsigned, typedefed types, etc. If you call void a keyword, then id is too.
A look at the Objective-C Programming Language gives you some clues about what is possible or not. As some "keywords" are defined in a header file, their substitution can lead to observed (and unexpected) behavior.
You could check out The Objective-C Programming Language.
As far as I can tell, that document doesn't classify anything as a "reserved word". The #implementation, #interface, etc. are called "compiler directives."