Passing function as argument to Objective-C method - objective-c

I'd like to pass a C function as an argument to an Objective-C method, to then act as a callback. The function has type int (*callback)(void *arg1, int arg2, char **arg3, char **arg4).
I keep getting the syntax wrong. How do I do this?

As a slightly more complete alternative to KKK4SO's example:
#import <Cocoa/Cocoa.h>
// typedef for the callback type
typedef int (*callbackType)(int x, int y);
#interface Foobar : NSObject
// without using the typedef
- (void) callFunction:(int (*)(int x, int y))callback;
// with the typedef
- (void) callFunction2:(callbackType)callback;
#end
#implementation Foobar
- (void) callFunction:(int (*)(int x, int y))callback {
int ret = callback(5, 10);
NSLog(#"Returned: %d", ret);
}
// same code for both, really
- (void) callFunction2:(callbackType)callback {
int ret = callback(5, 10);
NSLog(#"Returned: %d", ret);
}
#end
static int someFunction(int x, int y) {
NSLog(#"Called: %d, %d", x, y);
return x * y;
}
int main (int argc, char const *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Foobar *baz = [[Foobar alloc] init];
[baz callFunction:someFunction];
[baz callFunction2:someFunction];
[baz release];
[pool drain];
return 0;
}
Basically, it's the same as anything else, except that without the typedef, you don't specify the name of the callback when specifying the type of the parameter (the callback parameter in either callFunction: method). So that detail might have been tripping you up, but it's simple enough.

The following peice of code worked,absolutely fine . just check
typedef int (*callback)(void *arg1, int arg2, char **arg3, char **arg4);
int f(void *arg1, int arg2, char **arg3, char **arg4)
{
return 9;
}
-(void) dummy:(callback) a
{
int i = a(NULL,1,NULL,NULL);
NSLog(#"%d",i);
}
-(void) someOtherMehtod
{
callback a = f;
[self dummy:a];
}

Related

ObjC: proper use of property and synthesize?

Does anyone know why this code is running into compilation errors? I'm compiling it on Catalina using clang. I posted a similar issue here but that was when I was trying to compile using clang on Linux. I thought getA and setA are auto-generated by synthesize. Thanks!
#import <Foundation/Foundation.h>
#interface A: NSObject
#property int a;
#end
#implementation A
{
int a;
}
#synthesize a;
#end
int main (int argc, char * argv[])
{
#autoreleasepool {
A *a = [[A alloc] init];
[a setA:99];
int v = [a getA];
NSLog (#" %d\n", v);
}
return 0;
}
Compilation:
$ clang -framework Foundation otest0.m -o hello
otest0.m:23:16: warning: instance method '-getA' not found (return type defaults
to 'id') [-Wobjc-method-access]
int v = [a getA];
^~~~
otest0.m:3:12: note: receiver is instance of class declared here
#interface A: NSObject
^
otest0.m:23:9: warning: incompatible pointer to integer conversion initializing
'int' with an expression of type 'id' [-Wint-conversion]
int v = [a getA];
^ ~~~~~~~~
2 warnings generated.
The getter/setter pair is synthesized as
-(int)a;
-(void)setA:(int)val;
So you need:
int main (int argc, char * argv[])
{
#autoreleasepool {
A *a = [[A alloc] init];
[a setA:99];
int v = [a a];
NSLog (#" %d\n", v);
}
return 0;
}
Declaring a property with name a produces a getter with name a, not getA.
This is what the first warning is about: "instance method '-getA' not found"
This works on my system (macOS):
#import <Foundation/Foundation.h>
#interface A: NSObject
#property int a;
#end
#implementation A {
int a;
}
#synthesize a;
-(int) getMyValue {
return a;
}
#end
int main () {
#autoreleasepool {
A *a = [[A alloc] init];
[a setA:99];
NSLog (#"value = %d", [a getMyValue]);
}
return 0;
}
If file is saved as synth.m the terminal command is: clang synth.m -framework Foundation -o synth && ./synth

Objc runtime set ivar with value

I'm trying to dynamically create a class with data loaded from JSON. I have successfully created the class, but still stuck at how to add several constants with the values loaded from the JSON.
For example, the JSON below:
"Josh": {
"age": 10
}
I want a class like
class Josh {
int age = 10;
}
However, I can only achieve
class Josh {
int age;
}
with the following code:
Class Josh = objc_allocateClassPair([NSObject class], "Josh", 0);
class_addIvar(Josh, "age", sizeof(int), rint(log2(sizeof(int))), #encode(int));
objc_registerClassPair(Josh);
// Below is the desired result
Josh j = [[Josh alloc] init];
NSLog([j age]); // prints 10
So can anybody answer me, how to add the primitive value 10 to this Ivar I've added? Thank you
What you are doing is almost certainly madness and you shouldn't be messing with it.
That said, the code you wrote doesn't even compile.
So let's fix it.
You can't declare Josh j, because Josh is a variable, not a type. Instead you can use id, which means “any Objective-C object”.
id j = [[Josh alloc] init];
You also can't say [j age] unless you declare an age method somewhere, so stick this somewhere:
#protocol Dummy <NSObject>
- (int)age;
#end
You also can't say NSLog([j age]) because [j age] returns an int, not an NSString *. So do this instead:
NSLog(#"%d", [j age]);
Now you'll find that you get an “unrecognized selector” error, because you didn't add an age method to your Josh class. To do that, you first have to import the Objective-C runtime:
#import <objc/runtime.h>
Then you can add an age method like this:
Ivar ageIvar = class_getInstanceVariable(Josh, "age");
ptrdiff_t ageIvarOffset = ivar_getOffset(ageIvar);
IMP ageImp = imp_implementationWithBlock(^int(id self) {
void *base = (__bridge void *)self;
return *(int *)((unsigned char *)base + ageIvarOffset);
});
class_addMethod(Josh, #selector(age), ageImp, "v#:");
Here's the complete, compilable, non-crashing program:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#protocol Dummy <NSObject>
- (int)age;
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
Class Josh = objc_allocateClassPair([NSObject class], "Josh", 0);
class_addIvar(Josh, "age", sizeof(int), rint(log2(sizeof(int))), #encode(int));
Ivar ageIvar = class_getInstanceVariable(Josh, "age");
ptrdiff_t ageIvarOffset = ivar_getOffset(ageIvar);
IMP ageImp = imp_implementationWithBlock(^int(id self) {
void *base = (__bridge void *)self;
return *(int *)((unsigned char *)base + ageIvarOffset);
});
class_addMethod(Josh, #selector(age), ageImp, "v#:");
objc_registerClassPair(Josh);
// Below is the desired result
id j = [[Josh alloc] init];
NSLog(#"%d", [j age]); // prints 10
}
return 0;
}
But it still just prints “0“:
2018-06-25 15:07:53.179809-0500 madness[35050:5762222] 0
Program ended with exit code: 0
If you want to initialize age to 10, you need to override init in your Josh class. If you were writing the init method normally, it would look like this:
- (instancetype)init {
if (self = [super init]) {
_age = 10;
}
return self;
}
But we can only send to super in something the compiler recognizes as a method body, and a block (like we'll pass to imp_implementationWithBlock) doesn't count as a method body. So we have to do the [super init] part the hard way, by calling objc_msgSendSuper. To do that, we have to import another header:
#import <objc/message.h>
Then we can write the block that implements init:
IMP initImp = imp_implementationWithBlock(^id(id self) {
struct objc_super superInfo = {
.receiver = self,
.super_class = NSObject.self
};
id (*msgSendSuper)(struct objc_super *, SEL) = (id (*)(struct objc_super *, SEL))objc_msgSendSuper;
self = msgSendSuper(&superInfo, #selector(init));
if (self) {
void *base = (__bridge void *)self;
*(int *)((unsigned char *)base + ageIvarOffset) = 10;
}
return self;
});
class_addMethod(Josh, #selector(init), initImp, "##:");
Here's the complete program:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
#protocol Dummy <NSObject>
- (int)age;
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
Class Josh = objc_allocateClassPair([NSObject class], "Josh", 0);
class_addIvar(Josh, "age", sizeof(int), rint(log2(sizeof(int))), #encode(int));
Ivar ageIvar = class_getInstanceVariable(Josh, "age");
ptrdiff_t ageIvarOffset = ivar_getOffset(ageIvar);
IMP ageImp = imp_implementationWithBlock(^int(id self) {
void *base = (__bridge void *)self;
return *(int *)((unsigned char *)base + ageIvarOffset);
});
class_addMethod(Josh, #selector(age), ageImp, "v#:");
IMP initImp = imp_implementationWithBlock(^id(id self) {
struct objc_super superInfo = {
.receiver = self,
.super_class = NSObject.self
};
id (*msgSendSuper)(struct objc_super *, SEL) = (id (*)(struct objc_super *, SEL))objc_msgSendSuper;
self = msgSendSuper(&superInfo, #selector(init));
if (self) {
void *base = (__bridge void *)self;
*(int *)((unsigned char *)base + ageIvarOffset) = 10;
}
return self;
});
class_addMethod(Josh, #selector(init), initImp, "##:");
objc_registerClassPair(Josh);
// Below is the desired result
id j = [[Josh alloc] init];
NSLog(#"%d", [j age]); // prints 10
}
return 0;
}
And here's the output:
2018-06-25 15:31:41.837748-0500 madness[35369:5786038] 10
Program ended with exit code: 0

What is the Instance/object in the code?

Could Somebody please describe to me what the instance/object is in the following Code (objective-C). I'm confused because the (-) void before void means that its a method for an instance, but I don't know where the instance/object is.
#import <Foundation/Foundation.h>
//interface section
#interface Fraction: NSObject
- (void) print;
- (void)setNumerator: (int) n;
- (void)setDenominator: (int) d;
#end
//implementation section
#implementation Fraction
{
int numerator;
int denominator;
}
-(void) print;
{
NSLog(#"%i/%i",numerator,denominator);
}
-(void) setNumerator:(int)n
{
numerator = n;
}
-(void)setDenominator:(int)d
{
denominator = d;
}
#end
//program section
int main(int argc, const char * argv[])
{
//this is a program to work with fractions-class version
#autoreleasepool {
Fraction *myFraction;
//create an instance of a fraction
myFraction = [Fraction alloc];
myFraction = [myFraction init];
//set fraction to 1/3
[myFraction setNumerator:1];
[myFraction setDenominator:3];
//display the fraction via print methoD
NSLog(#"the value of myFraction is:");
[myFraction print];
}
return 0;
}
myFraction is the instance of the Fraction class. Instance methods have to be addressed to an instance rather than a class, and as you can see, print, setNumerator, and setDenominator are all addressed to myFraction.

CFDictionaryGetValue()

I could not able to get the value for the key.
I have a structure pt(which is the value) and a wTiId,wTiId1(which is the key).
I am sure that something wrong i'm doing in the below code but i could not figure out what
it is.
Timers.h
---------
#import <Foundation/Foundation.h>
struct session {
int a;
char c;
}pstruct;
#interface Timers : NSObject {
unsigned short wTiId;
unsigned short wTiId1;
}
-(void)timer;
#end
Timers.m
--------
#import "Timers.h"
#implementation Timers
-(id)init
{
if (self=[super init]) {
wTiId=71;
wTiId1=72;
}
return self;
}
-(void)dealloc
{
[super dealloc];
}
-(void)timer
{
struct session* pt = &pstruct;
pt->a=12;
pt->c='L';
CFDictionaryValueCallBacks cbvs = {0,NULL,NULL,NULL,NULL};
CFMutableDictionaryRef cfmdict = CFDictionaryCreateMutable(NULL,0,&kCFTypeDictionaryKeyCallBacks,&cbvs);
NSLog(#"Dict size:%d\n",((CFIndex)CFDictionaryGetCount(cfmdict)));
CFNumberRef tiId = CFNumberCreate(NULL,kCFNumberShortType,&wTiId);
CFNumberRef tiId1 = CFNumberCreate(NULL,kCFNumberShortType,&wTiId1);
CFDictionarySetValue(cfmdict,tiId,pt);
NSLog(#"Dict size:%d\n",((CFIndex)CFDictionaryGetCount(cfmdict)));
CFDictionarySetValue(cfmdict,tiId1,pt);
NSLog(#"Dict size:%d\n",((CFIndex)CFDictionaryGetCount(cfmdict)));
NSLog(#"The value is:%s",(CFDictionaryGetValue(cfmdict,tiId)));
CFRelease(tiId);
CFRelease(tiId1);
}
#end
main.m
------
#import <Foundation/Foundation.h>
#import "Timers.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Timers* time = [[Timers alloc]init];
[time timer];
[pool drain];
return 0;
}
output
------
2011-05-15 14:52:54.857 timer[3511:a0f] Dict size:0
2011-05-15 14:52:54.861 timer[3511:a0f] Dict size:1
2011-05-15 14:52:54.861 timer[3511:a0f] Dict size:2
2011-05-15 14:52:54.862 timer[3511:a0f] The value is:
I tried with the format specifier "%#" also.Nothing gets printed when CFDictionaryGetValue
() function is called.The return type of this function is const void*.
The result of CFDictionaryGetValue is what you put in the dictionary.
You have to cast the result of CFDictionaryGetValue to the right pointer type to access the structure members:
struct session *value = (struct session *) CFDictionaryGetValue(cfmdict,tiId);
NSLog(#"The value is %d and %c", value->a, value->c);
You cannot store structs in CFDictionarys, only pointers to structs will work. You cannot print the contents of the struct with the %s format specifier.

Logical Error-Objective C

I am not getting the correct output.
data.m
---------
#implementation data
- (id) initWithID:(int) uMessageId withData:(id)pData withSize:(size_t) uDataSize
{
//if(self=[super init])
//{
// Initialize the member variables
m_uSessionId = 0x00;
m_chSequenceChar= 0;
// Initialize values from derived class
m_nMessageId = uMessageId;
m_pData = (int*)pData;
m_uDataSize = (int)uDataSize;
//}
return self;
}
- (BOOL) TxCreateImage:(id) pData withLen:(id)uLen
{
sprintf((char *)pData,"%x%d%d%d",ASCII_STX,m_uSessionId,m_chSequenceChar,m_nMessageId);
//uLen = ENCODED_MSG_DATA_OFFSET;
NSLog(#"%s",pData);
return YES;
}
- (void)dealloc
{
[super dealloc];
}
#end
data_derived.m //derived class of data
--------------------
#implementation requestSession
- (id)init
{
char* pData[4096];
size_t asize = sizeof(st);
self=[super initWithID:ID withData:(id)pData withSize:asize];
if (self) {
data* dat = [[data alloc]init];
[dat TxCreateImage:self withLen:self];
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
#end
test2.m
---------
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
requestSession* session = [[requestSession alloc]init];
[pool drain];
return 0;
}
remote.h
-----------
#import <Foundation/Foundation.h>
struct RMH_REQUEST_SESSION_MSG //Message Data
{
int uDeviceID;
int uProtocolVersion;
int uReserved[5];
};
typedef enum
{
RM_REQUEST_SESSION = 0x11 //Message ID
} REMOTE_MESSAGE_ID;
#interface remote : NSObject {
}
#end
data.h
---------
#import <Foundation/Foundation.h>
#import "remote.h"
#interface data : NSObject {
#public
int m_nMessageId; //Message ID
int m_uSessionId; //Session ID
int m_chSequenceChar; //Sequence ID
int* m_pData; //Integer buffer to carry data
int m_uDataSize; //Datasize
}
- (id)initWithID:(int) uMessageId withData:(id)pData withSize:(size_t) uDataSize;
- (void)dealloc;
- (BOOL) TxCreateImage:(id) pData withLen:(id)uLen;
//+ (id)CreateMessage:(REMOTE_MESSAGE_ID)nMessageNumber;
//+ (id)CreateMessage:(const int*)szMessageName;
#end
data_derived.h
---------------------
#import <Foundation/Foundation.h>
#import "data.h"
#define DECLARE_RS232_NEWMSG(ClassID)\
enum \
{ \
ID = ClassID \
}; \
#interface requestSession : data {
#public
DECLARE_RS232_NEWMSG(RM_REQUEST_SESSION);
struct RMH_REQUEST_SESSION_MSG st;
int size;
}
-(id)init;
-(void)dealloc;
#end
The expected output is
2 0 0 17
. But its printing me
2 0 0 0
.
I need to pass the values assigned in
initWithData method
in data.m to
TxCreateMessage
in data.m.
My answer was irrelevant since I missed something in your question. the problem is that you are casting a int * to char *, pointer casting is the root to many many issues (in this case, it's probably an endianess problem.)
If you want to use sprintf, use it on char*, not int*:
- (BOOL) TxCreateImage:(id) pData withLen:(id)uLen
{
char printStr[30];
sprintf(printStr,"%x%d%d%d",ASCII_STX,m_uSessionId,m_chSequenceChar,m_nMessageId);
//uLen = ENCODED_MSG_DATA_OFFSET;
NSLog(#"%s",printStr);
return YES;
}