I have a function written in C that is rather large and currently is only running in the main() function of a C file while I have been using to test it out. Here is it's declaration
void getData(const char* encodedBuffer, int user);
If you would like to see the contents of getData Here is the pastebin for it.
As of now I'm just passing in encodedBuffer which I will have a global variable that is getting updated by the getData function.
I would like to know what the proper way is to turn a C based function like this into a Objective-C protocol method. Right now I'm setting it up like this
iDriver.h (my interface/protocol)
#import <Foundation/Foundation.h>
#protocol iDriver <NSObject>
-(void)getData;
-(void)cancel;
#end
DriverOne.h (Class that actually implements the protocol method)
#import <Foundation/Foundation.h>
#import "iDriver.h"
#interface DriverOne : NSObject <iDriver>
#end
DriverOne.m
#import "DriverOne.h"
#implementation DriverOne
enum Status getData(char* encodedBuffer, int user)
{
// Copy C code which I showed in the link earlier
// to above into this method. I will want it to return a status
// and populate a global variable with the data.
}
My thought is that I shouldn't really run into any issues doing this since Objective-C is a superset of C but just copying the C code into a method like that be problematic?
When porting a C function to an ObjC method, it's just a tweak to the signature. In your case, you'll change:
enum Status getData(char* encodedBuffer, int user)
to something like:
- (enum Status) getData: (char*) encodedBuffer user: (int) user
Objective C methods are always denoted with a - at the beginning (class methods start with a +). If you want to specify the type, you put in parens (otherwise it's assumed to be type id which is generic for "object"). And you have to keywordize your function name. One keyword for each argument.
Then just like C, you can just copy paste that line into your header file:
#protocol iDriver <NSObject>
- (enum Status) getData: (char*) encodedBuffer user: (int) user;
#end
After that just copy the guts inside the method.
It's not clear how familiar you are with the ideas behind OO and instances, but using it might look something like:
DriverOne *driverOneObject = [[DriverOne alloc] init];
char *buffer = malloc(YOUR_BUFFER_SIZE);
enumStatus status = [driverObject getData: buffer user: SomeUserThing];
Coming full circle, is there a reason you want this to be reified (turned into objects)? I'm all for lots of objects myself, but one of the features of ObjectiveC, is that it IS a superset of C. You can just use your function as is from your C prototype. You don't have to wrap it up in an ObjectiveC object, unless you see an advantage to doing so.
Related
Here is a code snippet from Learning objective-c 2.0
Full code:
ClassWithFloat.h
#import <Foundation/Foundation.h>
#interface ClassWithFloat : NSObject
{
float value;
}
-(void)setValue:(float)aValue;
#end
ClassWithFloat.m
#import "ClassWithFloat.h"
#implementation ClassWithFloat
-(void)setValue:(float)aValue
{
value = aValue;
}
#end
ClassWithInt.h
#import <Foundation/Foundation.h>
#interface ClassWithInt : NSObject
{
int value;
}
-(void)setValue:(int)aValue;
#end
ClassWithInt.m
#import "ClassWithInt.h"
#implementation ClassWithInt
-(void)setValue:(int)aValue
{
value = aValue;
}
#end
main.m:
#import <Foundation/Foundation.h>
#import "ClassWithFloat.h"
#import "ClassWithInt.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
id number = [[ClassWithInt alloc] init];
[number setValue:3];
}
return 0;
}
failed to compile, after changing to ClassWithInt* number it works.
Error message:
/Users/jcyangzh/src/oc/SameName/SameName/main.m:17:9: Multiple methods named 'setValue:' found with mismatched result, parameter type or attributes
But since objective-c is somehow a dynamic programming language, the message call will be translated to native C method call.
obj_msgSend(number, #selector(setValue:), 3)
the obj_msgSend method find the class structure for the number object by isa variable. Which should make no difference between id or ClassWithInt type.
Why objective-c compiler could not recognize the right method?
Note: I am asking this question, because having same method name, but different argument type for different class is reasonable to me. But it seems that it is not possible either because the compiler limitation or the language design (do not supporting method overloading etc).
The problem really is that your object is only typed as id within the lexical scope.
The compiler doesn't know which method of the same name/selector to use.
You have multiple classes that have that selector but with different signatures because their arguments are different types.
You should avoid id in this case
Or typecast your object in the message send brackets to tell the compiler what class's method to use
Or
Bracket the same message call repeatedly in a sequence of if ([obj isKindOf:
checks. (Crazy here)
Or
Best take a hint from NSNumber class on good method naming conventions and do something like setFloatValue: and setIntValue: which is more readable and clear and helps the compiler a bit.
But any time you have and id type only, you need to be checking if the object isKindOf: or you are asking for trouble.
It is very very bad to have methods with same name but different signatures. (It is documented somewhere but I can't find now)
The calling conversion between calling setValue:(float) is different to setValue:(int), compiler have to generate different binary code.
As you said, it end up with something like
obj_msgSend(number, #selector(setValue:), 3)
but they are different
obj_msgSend(number, #selector(setValue:), (int)3)
obj_msgSend(number, #selector(setValue:), (float)3.0f)
Compiler have to decide at compile-time to generate the which version. Because the calling conversion between pass parameter with int type and float type are different.
Given code
ClassWithInt *number = [[ClassWithInt alloc] init];
[number setValue:3];
Compile know it need to generate the version with int with the help of type information.
but without type information
id number = [[ClassWithInt alloc] init];
[number setValue:3]; // is this takes int or float? if it is float then 3 need to be convert to float value first
There are two possible way to call it. Compiler can't figure it out without help. Hence the error message.
I have a chunk of code written in C that is pulling data from a device, that code can be viewed Here
I want this code which contains a function called getData to be run as method (called getData) of an Objective-C class rather than just having it run from inside the main() C function as it does now while I test it out. My goal is for this method to populate a public global variable variable or even just an class property with a base64 encoded string and return a status.
Here Is how I'm currently setting this up, but this is also my first time writing both C and Objective-C so to be honest I'm not sure if my approach is correct. First I create an interface(protocol) called GDDriver.h
//GDDriver.h
typedef enum Status : NSInteger {
Success,
Cancelled,
DeviceNotFound,
DeviceError,
UnkownModel,
} Status;
#protocol GWDriver <NSObject>
-(enum Status)getData;
-(void)cancel;
#end
I then have a class which lets call it DriverOne which I'm setting up like this
DriverOne.h
// DriverOne.h
#import <Foundation/Foundation.h>
#import "GWDriver.h"
#interface DriverOne : NSObject <GWDriver>
#end
DriverOne.m
// DriverOne.m
#import "DriverOne.h"
#implementation DriverOne
enum Status getData(char* encodedBuffer, int user)
{
// Copy C code which I showed in the link earlier
// into this method. I will want it to return a status
// and populate a global variable with the data.
}
void cancel()
{
// Cancels and closes driver
// is called from with in getData()
}
#end
I'm aware that the methods are currently written in C syntax here, I'm not sure if that is bad practice or not though at this point. Here is how I intend to call the method.
DriverOne *driver = [[DriverOne alloc] init];
driver.getData();
Am I completely off base here or is this approach correct in what I'm trying to achieve?
Thanks for any advice or suggestions.
Best practice dictates that you generally don't use C style functions in your Objective C classes.
char pointers are also frowned upon, generally. I would change your functions to something like this:
- (enum Status)getDataWithBuffer:(NSString *)buffer userId:(NSInteger)userId
{
char * encodedBuffer = [buffer UTF8String];
// Copy C code which I showed in the link earlier
// into this method. I will want it to return a status
// and populate a global variable with the data.
}
- (void)cancel
{
// Cancels and closes driver
// is called from with in getData()
}
and then change your call to this
DriverOne *driver = [[DriverOne alloc] init];
[driver getData:#"your data" userId:12345];
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.
i am having objective c class, which i have a C function in it.
in that C function i want to call to ANOTHER c function in another class.
in the first class C function , i do this :
HelloWorldLayer *ran;
ran=[[HelloWorldLayer alloc] init];
[ran showDigital:BinaryWord];
when HelloWorldLayer is the other class, which have the C function :
void showDigital( int word[4])
and it declared also in the HelloWorldLayer.h .
in the first class, when i am trying to call the other function it says that showDigital function is not found.
is it has to do with the fact that its a C function ?
thanks.
Do you want to call a C function or an Objective-C method? You've got your terminology all muddled. I think you want to define an Objective-C method on HelloWorldLayer and call that, so here is what your header should look like:
HelloWorldLayer.h
#interface HelloWorldLayer : NSObject
- (void)sendDigital:(int[4])word;
#end
Then you can call that method by doing what you have already tried.
You can't declare a C function inside an Objective-C class. You could define it in your #interface block, but it wouldn't be part of that Objective-C class. Only Objective-C methods can be part of the class.
So for instance if you really really really wanted you could do:
HelloWorldLayer.h
#interface HelloWorldLayer : NSObject
void sendDigital(int[4] word);
#end
But when you called sendDigital you'd have to do it like this:
int word[4] = {1,2,3,4};
sendDigital(word);
And you'll notice there's no HelloWorldLayer around at all. Now do you understand the difference here? I really do think you need to define your method as an Objective-C method rather than a C function.
First of all, the C function is not in the other class, it is merely in the same file as the other class. Objective-C classes have methods not functions.
So you can call your C function from anywhere provided you have an extern declaration of it. e.g. in HelloWorldLayer.h
extern void showDigital(int* word);
If you really want the function to be associated with instances of your class, you need to make it an Objective-C method. You can have that method be a thin wrapper for the original function if you like:
-(void) showDigital: (int*) word
{
showDigital(word);
}
If you want to pretend that your C function is part of an object, you'll need to add the receiver object as a parameter. You still won't be able to access private instance variables directly though.
void showDigital(id self, int* word)
{
[self foobar];
}
I'm writing an Objective-C wrapper for a C library and having problems accessing the references of my ivars.
The C library requires that I specify a function pointer that handles events, I specify it during the initialization of my Objective-C class.
- (id) init {
[super init];
RegisterClient(&handleEvent);
return self;
}
The C library is able to start searching for something and will then call the handleEvent function in case anything happened during the search. The function (basically) looks like this.
int handleEvent(void *Event) {
[delegate didFinishSearching];
return 0;
}
At least I'd wish it looked like this. The problem is that the code won't build because 'delegate' undeclared (first use in function) (of course I have declared it, I can call [delegate didFinishSearching] from any Objective-C method but not from a C function). Older questions from stackoverflow suggest to define an additional variable (e.g. theDelegate) in the header file:
id theDelegate;
#interface Controller : NSObject {
id delegate;
}
#property (assign) id delegate;
#end
Then, whenever I change the value of delegate to a new one, I have to assign this value to theDelegate, too.
Since my C is somewhat rusty, here are my questions:
1) Can I pass the RegisterClient C function a pointer to an Objective-C method instead of a pointer to a function as an argument in order to avoid the C function handleEvent completely?
2) If not: When I create multiple instances of this Objective-C class, will theDelegate be the same for all instances? (After all, it's not declared as an instance variable...)
Objective-C methods are C functions, but they have two hidden parameters at the front, so they won't have the int f(void *) signature.
What you probably want to do is use an libffi closure. That allows you to create a function with exactly the signature that you want, but that also has a pointer to your object passed along with it. See the example in the ffi_prep_closure man page. Your handleEvent function would probably then change to look something like this:
static void handleEventClosure(ffi_cif * cif, void * result, void ** args, void * userdata)
{
// Arguments.
void * Event = *args[0];
// Closed-over data.
id delegate = (id)userdata;
// Execute the method.
[delegate didFinishSearching];
// Smaller than sizeof(long), so use ffi_arg or ffi_sarg (unsigned or signed).
*(ffi_sarg *)result = (ffi_sarg)0;
}
Most of the time C Libraries like the one you describe accept a "userinfo" parameter conveniently size to match a pointer. You can use this to your advantage by passing your object as this "userinfo" parameter.
Then in callbacks, you cast the pointer back to an object and make the calls you need.