In one header file, I have something like:
// PasscodeInputViewController.h
typedef enum {
PasscodeInputModeOn, // set passcode
PasscodeInputModeEnter, // enter passcode
PasscodeInputModeChange, // change passcode
PasscodeInputModeOff // turn off passcode
} PasscodeInputMode;
In another header file, I declare a method that takes an argument of type PasscodeInputMode:
#import "PasscodeInputViewController.h"
- (void)presentPasscodeInputWithMode:(PasscodeInputMode)mode;
As you can see, I use #import "PasscodeInputViewController.h" as above so that PasscodeInputMode is recognized, but is there a #class equivalent for typedef enum?
No, there isn’t an equivalent.
Enumerations in Objective-C are the same as enumerations in C. Since the underlying type of an enumeration is implementation-dependent (e.g., it could be char or int), the compiler must know the complete declaration of the enumeration.
That said, a type specifier
enum nameOfEnum
without listing the enumeration members is valid C provided it appears after the type it specifies is complete, i.e., enum nameOfEnum { … } must appear beforehand in the translation unit.
In summary: There’s no forward declaration of enumerations, only backward references.
#Caleb, #Bavarious:
Most recent way (Jan, 2017) to forward declare the enum (NS_ENUM/NS_OPTION) in objective-c is to use the following:
// Forward declaration for XYZCharacterType in other header say XYZCharacter.h
typedef NS_ENUM(NSUInteger, XYZCharacterType);
// Enum declaration header: "XYZEnumType.h"
#ifndef XYZCharacterType_h
#define XYZCharacterType_h
typedef NS_ENUM(NSUInteger, XYZEnumType) {
XYZCharacterTypeNotSet,
XYZCharacterTypeAgent,
XYZCharacterTypeKiller,
};
#endif /* XYZCharacterType_h */`
Similar question Forward-declare enum in Objective-C
Forward declaration of classes is necessary to enable two classes to refer to each other. It's not uncommon to have two classes that are defined in terms of each other:
#class ClassB;
#interface ClassA : NSObject
{
ClassB *objectB;
}
#end
#interface ClassB : NSObject
{
ClassA *objectA;
}
#end
There's no way to make that compile without the forward declaration.
The same is not true of enumerations. enum just creates a set of named values... you can't include one enumeration in the definition of another. Therefore, there's never a need to forward declare an enumeration.
I think what you want is a class that has PasscodeInputMode as a property of it. That way you can be passing around an instantiated object of that class, and can set/get that property, and do other object-like-things with it (assume that's why you'd want to find a "#class equivalent"
Related
I have an Objective-C header that has to be used by a Swift class. However, this header has to use the Swift.h file for an enum declared in a Swift file. In other words, the setup is as follows:
MPViewController.h
#import "MyProject-Swift.h"
#interface MPViewController: UIViewController
#property (nonatomic, assign) MPSomeEnum theEnum;
...
#end
MyProject-Bridging-Header.h
...
#import "MPViewController.h"
...
SomeEnum.swift
#objc enum MPSomeEnum: Int {
...
}
When compiling the code, I get three errors:
'MyProject-Swift.h' file not found
Failed to emit precompiled header [Xcode DerivedData folder]/[...]/MyProject-Bridging-Header-swift_[...].pch for bridging header [Project folder]/MyProject-Bridging-Header.h
Unknown type name 'MPSomeEnum'
Am I correct to assume that this stems from the circular reference between MyProject-Swift.h and the bridging header MyProject-Bridging-Header.h? From looking at a similar question one solution is to use forward declaration. However, it doesn't seem possible to forward declare an enum, so perhaps the only way to do this is to move the enum definition to an Objective-C file altogether?
TL&DR; As you suspected, you need to either move the enum declaration to Objective-C, or migrate the class to Swift.
Forward declarations of enums is possible in Objective-C:
#property SomeEnum someProperty;
- (void)doSomethingWithEnum:(enum SomeEnum)enumValue;
However correct Cocoa enums are typedefs to NSInteger: typedef NS_ENUM(NSInteger, MyEnum), and the enum keyword doesn't hold enough information for how much space to allocate when using it, so you'll get into all kind of compiler error when you want to use declarations like this. Thus an enum declared in Swift is not forward declarable in Objective-C.
Now, if you really want to keep the enum definition in Swift, you could use a workaround, and declare it as NSInteger in Objective-C, while providing a specialized property in Swift:
// NS_REFINED_FOR_SWIFT imports this in Swift as __theEnum
#property(nonatomic, assign) NSInteger theEnum NS_REFINED_FOR_SWIFT;
extension MPViewController {
// we provide a wrapper around the Objective-C property
var theEnum: MPSomeEnum {
// this uses a forced unwrap, beware :)
return MPSomeEnum(rawValue: theEnum)!
}
}
I would like to avoid importing AVFoundation framework in my header but I need to declare CMTime.
For NSObject I can do #class AVPlayer; and import everything in the .m file. How to do this with a struct like CMTime?
If you need to reference CMTime struct in the header, you need to include <CMTime.h>: forward-declaring a struct lets you use the name of the type in declarations of pointers, but not in declarations of members of that struct type.
In other words, you can do this:
struct CMTime; // Forward declaration of the struct
#interface MyInterface : NSObject
-(void)fillCmTime:(CMTime*)buffer;
#end
but you cannot do this:
struct CMTime; // Forward declaration of the struct
#interface MyInterface : NSObject {
// This is not allowed
CMTime time;
}
// This is not allowed either
-(CMTime)getTime;
#end
The reason that you can do #class AVPlayer and then use it in declarations of members is that Objective-C classes (id-types) are implemented as pointers. In fact, you cannot declare variables of an id-type without an asterisk.
Is there an Objective-C runtime library function (unlikely) or set of functions capable of inspecting static (quasi-class level) variables in Objective-C? I know I can utilize a class accessor method but I'd like to be able to test without writing my code "for the test framework".
Or, is there a obscure plain C technique for external access to static vars? Note this information is for unit testing purposes—it needn't be suitable for production use. I'm conscious that this'd go against the intent of static vars... a colleague broached this topic and I'm always interested in digging into ObjC/C internals.
#interface Foo : NSObject
+ (void)doSomething;
#end
#implementation Foo
static BOOL bar;
+ (void)doSomething
{
//do something with bar
}
#end
Given the above can I use the runtime library or other C interface to inspect bar? Static variables are a C construct, perhaps there's specific zone of memory for static vars? I'm interested in other constructs that may simulate class variables in ObjC and can be tested as well.
No, not really, unless you are exposing that static variable via some class method or other. You could provide a + (BOOL)validateBar method which does whatever checking you require and then call that from your test framework.
Also that isn't an Objective-C variable, but rather a C variable, so I doubt there is anything in the Objective-C Runtime that can help.
The short answer is that accessing a static variable from another file isn't possible. This is exactly the same problem as trying to refer to a function-local variable from somewhere else; the name just isn't available. In C, there are three stages of "visibility" for objects*, which is referred to as "linkage": external (global), internal (restricted to a single "translation unit" -- loosely, a single file), and "no" (function-local). When you declare the variable as static, it's given internal linkage; no other file can access it by name. You have to make an accessor function of some kind to expose it.
The extended answer is that, since there is some ObjC runtime library trickery that we can do anyways to simulate class-level variables, we can make make somewhat generalized test-only code that you can conditionally compile. It's not particularly straightforward, though.
Before we even start, I will note that this still requires an individualized implementation of one method; there's no way around that because of the restrictions of linkage.
Step one, declare methods, one for set up and then a set for valueForKey:-like access:
// ClassVariablesExposer.h
#if UNIT_TESTING
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#define ASSOC_OBJ_BY_NAME(v) objc_setAssociatedObject(self, #v, v, OBJC_ASSOCIATION_ASSIGN)
// Store POD types by wrapping their address; then the getter can access the
// up-to-date value.
#define ASSOC_BOOL_BY_NAME(b) NSValue * val = [NSValue valueWithPointer:&b];\
objc_setAssociatedObject(self, #b, val, OBJC_ASSOCIATION_RETAIN)
#interface NSObject (ClassVariablesExposer)
+ (void)associateClassVariablesByName;
+ (id)classValueForName:(char *)name;
+ (BOOL)classBOOLForName:(char *)name;
#end
#endif /* UNIT_TESTING */
These methods semantically are more like a protocol than a category. The first method has to be overridden in every subclass because the variables you want to associate will of course be different, and because of the linkage problem. The actual call to objc_setAssociatedObject() where you refer to the variable must be in the file where the variable is declared.
Putting this method into a protocol, however, would require an extra header for your class, because although the implementation of the protocol method has to go in the main implementation file, ARC and your unit tests need to see the declaration that your class conforms to the protocol. Cumbersome. You can of course make this NSObject category conform to the protocol, but then you need a stub anyways to avoid an "incomplete implementation" warning. I did each of these things while developing this solution, and decided they were unnecessary.
The second set, the accessors, work very well as category methods because they just look like this:
// ClassVariablesExposer.m
#import "ClassVariablesExposer.h"
#if UNIT_TESTING
#implementation NSObject (ClassVariablesExposer)
+ (void)associateClassVariablesByName
{
// Stub to prevent warning about incomplete implementation.
}
+ (id)classValueForName:(char *)name
{
return objc_getAssociatedObject(self, name);
}
+ (BOOL)classBOOLForName:(char *)name
{
NSValue * v = [self classValueForName:name];
BOOL * vp = [v pointerValue];
return *vp;
}
#end
#endif /* UNIT_TESTING */
Completely general, though their successful use does depend on your employment of the macros from above.
Next, define your class, overriding that set up method to capture your class variables:
// Milliner.h
#import <Foundation/Foundation.h>
#interface Milliner : NSObject
// Just for demonstration that the BOOL storage works.
+ (void)flipWaterproof;
#end
// Milliner.m
#import "Milliner.h"
#if UNIT_TESTING
#import "ClassVariablesExposer.h"
#endif /* UNIT_TESTING */
#implementation Milliner
static NSString * featherType;
static BOOL waterproof;
+(void)initialize
{
featherType = #"chicken hawk";
waterproof = YES;
}
// Just for demonstration that the BOOL storage works.
+ (void)flipWaterproof
{
waterproof = !waterproof;
}
#if UNIT_TESTING
+ (void)associateClassVariablesByName
{
ASSOC_OBJ_BY_NAME(featherType);
ASSOC_BOOL_BY_NAME(waterproof);
}
#endif /* UNIT_TESTING */
#end
Make sure that your unit test file imports the header for the category. A simple demonstration of this functionality:
#import <Foundation/Foundation.h>
#import "Milliner.h"
#import "ClassVariablesExposer.h"
#define BOOLToNSString(b) (b) ? #"YES" : #"NO"
int main(int argc, const char * argv[])
{
#autoreleasepool {
[Milliner associateClassVariablesByName];
NSString * actualFeatherType = [Milliner classValueForName:"featherType"];
NSLog(#"Assert [[Milliner featherType] isEqualToString:#\"chicken hawk\"]: %#", BOOLToNSString([actualFeatherType isEqualToString:#"chicken hawk"]));
// Since we got a pointer to the BOOL, this does track its value.
NSLog(#"%#", BOOLToNSString([Milliner classBOOLForName:"waterproof"]));
[Milliner flipWaterproof];
NSLog(#"%#", BOOLToNSString([Milliner classBOOLForName:"waterproof"]));
}
return 0;
}
I've put the project up on GitHub: https://github.com/woolsweater/ExposingClassVariablesForTesting
One further caveat is that each POD type you want to be able to access will require its own method: classIntForName:, classCharForName:, etc.
Although this works and I always enjoy monkeying around with ObjC, I think it may simply be too clever by half; if you've only got one or two of these class variables, the simplest proposition is just to conditionally compile accessors for them (make an Xcode code snippet). My code here will probably only save you time and effort if you've got lots of variables in one class.
Still, maybe you can get some use out of it. I hope it was a fun read, at least.
*Meaning just "thing that is known to the linker" -- function, variable, structure, etc. -- not in the ObjC or C++ senses.
Currently I am working on a cocos2d+Box2D project so I have deal with some Objective-C++ code.
And I am facing to such situation:
#import "cocos2d.h"
#import "Box2D.h"
#interface BasicNode : CCNode {
#private
ccColor3B _color;
b2Body *_body;
b2Fixture *_shape;
}
b2Body and b2Fixture are C++ class that defined in Box2D.h
It works if the implementation of BasicNode is named BasicNode.mm.
But if I have another file named Game.m that is using BasicNode and import BasicNode.h, it won't compile because .m file is Obj-C file and does not know about C++ code.
So I decided to move #import "Box2D.h" into implementation file and only keep type declaration in head file (this is exactly what header file should contain).
But how do I do it? They are C++ class type but they are actually just a pointer so I wrote some helper macro
#ifdef __cplusplus
#define CLS_DEF(clsname) class clsname
#else
#define CLS_DEF(clsname) struct clsname; typedef struct clsname clsname
#endif
CLS_DEF(b2Body);
CLS_DEF(b2Fixture);
It works, only if CLS_DEF(b2Body) is appear once only. Otherwise compiler will find multiple type declaration for a same name even they are the same. Than I have to change to
#ifdef __cplusplus
#define CLS_DEF(clsname) class clsname
#else
#define CLS_DEF(clsname) #class clsname
#endif
And it is working now.
But I don't think it is a great idea that I declare a C++ class type as an Obj-C class especially I am using ARC.
Is any better way do deal with it? And I don't really want to make something like this
#interface BasicNode : CCNode {
#private
ccColor3B _color;
#ifdef __cplusplus
b2Body *_body;
b2Fixture *_shape;
#else
void *_body;
void *_shape;
#endif
}
Edit: Also please tell me will my tweak way introduce any problem?? by making C++ class ivar looks like Obj-C class for other pure Obj-C code.
One simple solution is to rename Game.m to Game.mm.
There are a couple of ways. If you can rely on using the Objective-C 2.2 runtime's features, you can add ivars in class (category) extensions. This means you can add ivars in your class's .mm file, and keep the .h file clean of any C++ stuff.
If you need to support older versions of the runtime, there are a few ways to do it which are better than #ifdefing. In my opinion, the best way is to use the 'pimpl' idiom which is common in C++ - you forward declare an implementation struct in your header, and add an ivar which is a pointer to such a struct. In your class's implementation (.mm), you actually define that struct with all its C++ members. You then just need to allocate that implementation object in your init... method(s) with new and delete it in dealloc.
I've written up the pimpl idiom as it applies to cleanly mixing Objective-C and C++ in this article - it also shows some other potential solutions which you could consider.
With Xcode 5, you don't have to declare instance variables in the header file, you can just declare them in the implementation file. So your BasicNode header file is not "contaminated" with C++.
You can use "struct" instead of "class" in C++. The only difference is that in a class all members are private by default, while in a struct they are public by default. But you can do everything with a struct that you can do with a class. That way you can write for example
struct b2Body;
struct b2Fixture;
outside your interface, and
{ ...
struct b2Body* _body;
...
}
in your interface.
It is my understanding that typedef enums are globally scoped, but if I created an enum outside of the #interface of RandomViewController.h, I can't figure out how to access it from OtherViewController.m. Is there a way to do this?
So...
"RandomViewController.h"
#import <UIKit/UIKit.h>
typedef enum {
EnumOne,
EnumTwo
}EnumType;
#interface RandomViewController : UIViewController { }
and then...
"OtherViewController.m"
-(void) checkArray{
BOOL inArray = [randomViewController checkArray:(EnumType)EnumOne];
}
In OtherViewController.m:
#import "RandomViewController.h"
And you shouldn't name your variable like the type. Rather name it myEnumOne, or whatever you like :)
BOOL inArray = [randomViewController checkArray:(EnumType)myEnumOne];
Can you show us the declaration of the checkArray method? In my understanding it should be
- (BOOL)checkArray:(EnumType)blabla;
You shouldn't need to typecast the argument to EnumType when calling the method (I'm assuming that it's of the type EnumType already).
Just import the header.