C preprocessor directive to conditionally compile method calls with square brackets - objective-c

I know I can use preprocessor macros to conditionally compile certain method calls, for example:
#if SOMETHING
#define fmod(...)
#endif
...
fmod(34.0, 452.0); //this line doesn't get compiled if SOMETHING != 0.
Can I use the same procedure to conditionally compile method calls with opening and closing brackets?
Say I'd like to conditionally compile all calls to the class MyClass:
[MyClass doSomething];
[MyClass doSomethingElse];
#define MyClass[...] produces:
[ doSomething];
And that's an error. Any thoughts?

This is a workaround rely on fact that calling method on nil is no operation
#interface MyClassImpl : NSObject
+ (void)doSomething;
#end
#if SOMETHING
#define MyClass MyClassImpl
#else
#define MyClass ((Class)Nil)
#endif

You won't have any luck overloading the square brackets with macros, but you can flesh out your macro to have the effect you want with different syntax. Conditionally define a macro that takes an argument. In one case, the macro will resolve to just the argument, in the other case, the macro will resolve to white space.
(Edited to use variadic macro)
#define COMPILE_CONDITIONAL
#if defined(COMPILE_CONDITIONAL)
#define conditional(...) __VA_ARGS__
#else
#define conditional(...)
#endif
Then your use cases would look like:
conditional(fmod(34.0, 452.0));
conditional(MyClass doSomething);
conditional(MyClass doSomethingElse);
You will probably end up using a shorter macro than "conditional" typing that on every line gets old fast.

Related

Objective C reflection for C-type vars

I have few extern variables declared in #interface block of my .h file. They are assigned in .m file. In one method of my #interface (preferable class method) I want to enumerate over these variables via reflection, but I have no any idea is it possible at all, and how is yes ?
Example of my code:
Consts.h
extern NSString *MyConst1;
extern NSString *MyConst2;
Consts.m
NSString *MyConst1 = nil;
NSString *MyConst2 = nil;
+ (void)load {
MyConst1 = ...;
MyConst2 = ...;
}
+ (void)someMethod {
// i could use class_copyIvarList for objective c properties/ivars,
// but is it possible to get that MyConts1 and MyConst2 via reflection ?
// I understand, that C have different namespace,
// and I have no experience with it.
}
Objective-C supports reflection for Objective-C objects. The reflection works because every Obj-C class contains the necessary metadata (property names, method names etc.).
Objective-C doesn't in any way change the C language. Objective-C runtime is basically implemented in C and the compiler only translates Objective-C into C function calls.
No, you cannot use reflection for global variables outside the context of any Objective-C class.
If you want reflection, wrap them into a class.
However, note, that using reflection is almost never a good solution. Usually it is abused to solve a problem that is caused by bad architecture.
There are no class variables in ObjC. Only instances can have variables in ObjC. Even if you declare a global between #interface or #implementation and #end, it will still just be a global, ObjC doesn't actually know about it.
Only the ObjC parts in a .m file support introspection. One alternative approach would be to create a singleton that has methods returning your constants, then you can enumerate its methods. Alternately, you could create a function/method that returns an array of your constants. To avoid duplication of your constants and array, you could use the X Macro technique:
#define OSC_TYPE_CONSTANTS X(MyConst1) \
X(MyConst2)
// Header:
#define X(n) extern NSString* n;
OSC_TYPE_CONSTANTS
#undef X
// Implementation:
#define OSC_STRINGIFY(n) #n
#define X(n) NSString* n = OSC_STRINGIFY(n);
OSC_TYPE_CONSTANTS
#undef X
#define X(n) n,
NSArray* gAllTypesArray = [NSArray arrayWithObjects: OSC_TYPE_CONSTANTS nil];
#undef X
What do you need this for? Are these constants compiled directly into your app, or is this in a framework from which they are exported? Depending on that, there may be solutions involving CFBundle's CFBundleGetDataPointerFromName or dlsym.

Accessing static variables that are simulating class variables from unit tests

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.

why warning with my performSelector

Below is a simple PerformSelector that sends a message to obj to perform the looping method. All works well but I get the following yellow warning.
PerformSelector may cause a leak because its selector is unknown.
#import "MyClass.h"
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
MyClass *obj = [[MyClass alloc]init];
SEL mySel = #selector(looping);
[obj performSelector:mySel];
}
return 0;
}
This warning does not make sense because the performSelector must be aware of mySel because the looping method does get called - any ideas whats going on ??
Update
MyClass.h
#import <Foundation/Foundation.h>
#interface MyClass : NSObject
-(void)looping;
#end
MyClass.m
#import "MyClass.h"
#implementation MyClass
-(void)looping{
NSLog(#"Hey, i'm looping");
}
#end
Update -- The Real Answer
This is ARC specific:
performSelector may cause a leak because its selector is unknown
In short, ARC uses information based on the naming conventions and any additional attributes bound to the selector. When accessing a selector by name and performing it via the performSelector: family of methods, that information is lost and the compiler is warning you that it must make some assumptions regarding reference counting because this information is stripped away.
In short, the specific program you posted is safe but you are encouraged to use an alternative which is ARC-friendly.
The Previous Response
A selector's declaration does not need to be visible to the current translation in order to call it.
The compiler is allowed to assume default types for parameters and return types for class and instance methods (id is the default).
There are several compiler warnings which can warn you of these shady actions.
You probably forgot to declare the selector looping in the #interface, or you may have omitted the colon, if it has arguments: looping: would be its name.
this warning is due to that u have not told the compiler where the selector resides, import the file where it is or add the selector to header file where it should be

declare obj-c class interface that contain c++ class type ivar

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.

How to make debug code using #ifdef directive. Objective-c

i want to make a question about developing using #ifdef directive.
I want do make some code in objective-c only for debug use, for example:
in main function do this:
#define DEBUG_LEVEL
in my interface do this:
#ifdef DEBUG_LEVEL
BOOL editorIsDragged;
BOOL editorIsSelected;
#endif
.... other property
#ifdef #DEBUG_LEVEL
#property (nonatomic, readwrite) BOOL editorIsDragged;
#property (nonatomic, readwrite) BOOL editorIsSelected;
#endif
then in my implementation do this:
#ifdef #DEBUG_LEVEL
#synthetize editorIsDragged, editorIsSelected;
#endif
but i receve an error because in synthetize editorIsDragged and editorIsSelected are not defined.
If i try to define my own setter/getter method I receive the same error, because my vars (editorIsDragged and editorIsSelected) does not exist for XCode!
I in C use this method for write only debug code, but in obj-c what i must use??
thank you!
Shouldn't you put your #define DEBUG_LEVEL in a header file that gets included in all places that needs to know it? Like setting in in the build settings or putting in the *.pch. And I'm not sure if that a typo but you have also #define #DEBUG_LEVEL (see the second hash?) in the code here.