Is it possible for a class to have a member function? - objective-c

I've currently got some code as follows:
static void callback(several parameters)
{
...
}
#implementation SomeClass
- (id) init
{
self = [super init];
if (self)
{
id ct = CTTelephonyCenterGetDefault();
CTTelephonyCenterAddObserver((__bridge CFNotificationCenterRef)(ct), NULL, callback, NULL, NULL, CFNotificationSuspensionBehaviorHold);
....
Within the callback function's body I want to update some values, and I'd rather add them as properties of SomeClass rather than be global variables.
So is it possible to make the callback function a member function of SomeClass?

If you're using a C API that takes a callback and you want one of your class's instance methods to be called, you have to use a C function as a trampoline. Along with the callback function, most such C APIs allow you to specify a user value that gets passed back as an argument to callback. You should pass the instance pointer as that user value. Then, your trampoline will just call an instance method on the object.
For example:
static void callback(void* userValue, /* other parameters... */)
{
MyClass* myObject = (__bridge MyClass*)userValue;
[myObject myMethod:/* other parameters passed along */];
}
You'll have to figure out if CTTelephonyCenterAddObserver() can take such a user value argument and which it is.
It doesn't much matter if callback() is declared inside or outside of the #implementation.

So is it possible to make the callback function a member function of
SomeClass
Not really. A method requires an object to act on. What you normally can do, depending on the API you're using, is to store a pointer to your SomeClass instance, and pass it as an additional parameter for the addObserver function.
Presumably one of the NULL arguments in your call to CTTelephonyCenterAddObserver is supposed to be additional context for the callback routine. You can convert a pointer to an object to a (void *) pointer when passing it to the CTTelephonyCenterAddObserver, and then convert it back to a (SomeClass *) inside the callback function.

UPDATE
See my comment below. My answer should not be the accepted answer.
Move callback inside your #implementation, like this:
#implementation
- (void)callback:(SomeType)parameter1 parameter2:(SomeOtherType)parameter2 {
// do stuff
}
...
#end
If you want your method to be public, you need to add it to your interface:
#interface
- (void)callback:(SomeType)parameter1 parameter2:(SomeOtherType)parameter2;
#end
Apple's Objective-C intro may help.

Related

Objective C - Call Method defined from a Macro

I'm trying to call a Method from Objective-C that has been defined using a Macro which wraps a Swift function. I'm getting the compilation error No visible #interface for 'NearbyMessages' declares the selector '__rct_export__disconnect' in Xcode when I try to call my Method though.
My Swift function looks like this:
// SomeInterface.swift
#objc
func disconnect() -> Void {
// (disconnect logic here)
}
The Objective-C code looks like this:
// SomeInterface.m
#interface RCT_EXTERN_REMAP_MODULE(SomeInterfaceNameJS, SomeInterfaceNamePrivate, NSObject)
RCT_EXTERN_METHOD(disconnect);
-(void) invalidate {
[self __rct_export__disconnect]; // No visible #interface for 'NearbyMessages' declares the selector '__rct_export__disconnect'
NSLog(#"GNM_BLE: invalidate!");
}
#end
The macro RCT_EXTERN_METHOD (from React's RCTBridgeModule.h) is defined as:
#define RCT_EXTERN_METHOD(method) \
_RCT_EXTERN_REMAP_METHOD(, method, NO)
#define _RCT_EXTERN_REMAP_METHOD(js_name, method, is_blocking_synchronous_method) \
+ (const RCTMethodInfo *)RCT_CONCAT(__rct_export__, RCT_CONCAT(js_name, RCT_CONCAT(__LINE__, __COUNTER__))) { \
static RCTMethodInfo config = {#js_name, #method, is_blocking_synchronous_method}; \
return &config; \
}
By reading this, upon compilation the Method should be called __rct_export__disconnect, right?
So why is it not possible to use [self __rct_export__disconnect] - How else can I call this method?
TL;DR: To answer the question, call [self.class __rct_export__disconnect];. But it is better (and an only future-proof way in case the __rct_export__ prefix changes in the next versions of React Native) is to call the disconnect() function directly.
Objective-C allows having two main types of methods.
First type is the instance method, which operates on an instance of the Objective-C class. In the body of this method, self is a pointer to the instance of the object on the heap, hence the name. Through the self pointer you can access other instance methods, instance variables etc. It is declared/defined with a minus (-) sign in the beginning.
Second type is the class method, which operates on class objects (a specific Obj-C construct). It is most commonly used when you'd use a static function in other languages (with some caveats and quirks related to inheritance of course). In the body of this method, self is a pointer to the class object. Through it you can call other class methods, but you don't have access to any concrete instances of the object.
The difference is explained well here.
In your case, this macro defines a class method:
#define _RCT_EXTERN_REMAP_METHOD(js_name, method, is_blocking_synchronous_method) \
+ (const RCTMethodInfo *)RCT_CONCAT(__rct_export__, RCT_CONCAT(js_name, RCT_CONCAT(__LINE__, __COUNTER__))) { \
static RCTMethodInfo config = {#js_name, #method, is_blocking_synchronous_method}; \
return &config; \
}
And this method is an instance method:
- (void)invalidate {
[self __rct_export__disconnect]; // No visible #interface for 'NearbyMessages' declares the selector '__rct_export__disconnect'
NSLog(#"GNM_BLE: invalidate!");
}
When you call [self __rct_export__disconnect];] from the instance method, the self points to the instance of your class. The __rct_export__disconnect is defined as a class method (see macro), and thus it is not defined on the instances of the object.

Unable to access Objective-C class instance variables in body of C function

I have a class MicController and give it an instance variable like this:
#implementation MicController {
AudioStreamBasicDescription streamFormat;
}
I initialize the object and attempt to create an audio unit using that ASBD variable to keep track of a stream format.
- (instancetype)init {
if (self = [super init]) {
[self setup];
}
return self;
}
- (void)setup {
CreateInputUnit();
}
However, inside CreateInputUnit() I seem to have no access to the variable.
void CreateInputUnit () {
stream... // nothing
}
No auto-completion, no syntax high-lighting, no compilation. Am I missing something?
For your class, it is possible to have multiple object instances, or none. A C function can't know which object (if one even exists) in which to look for instance variables, unless you either (1) pass the object as a parameter, or (2) set a global C variable to a pointer to the instantiation of the object of interest. The latter (2) is only useful for singleton objects, of course.
Given a pointer to an object passed to the C function, you can use the struct dereference operator ("->") to access any instance variable in the object that was declared in the class interface. Or you can just pass the data or pointers of interest directly.

Pass instance method as function pointer to C Library

I am writing an Objective-C application that uses a C Library. The issue which i am currently facing is that the C Library has a structure where some field are function pointers later used as callbacks. How can i convert an Objective-C instance method to a function pointer and pass it to the library?
You will need to provide the C callback function within the Objective-C class implementation file, and this will only work if the callback is able to use a context pointer of some sort.
So imagine the C callback signature is like this:
void myCallback(void *context, int someOtherInfo);
Then within the Objective-C class implementation file you need to use that callback to trampoline back into your Objective-C class (using the context pointer as the instance of the class to invoke):
// Forward declaration of C callback function
static void theCallbackFunction(void *context, int someOtherInfo);
// Private Methods
#interface MyClass ()
- (void)_callbackWithInfo:(int)someOtherInfo;
#end
#implementation MyClass
- (void)methodToSetupCallback
{
// Call function to set the callback function, passing it a "context"
setCallbackFunction(theCallbackFunction, self);
...
}
- (void)_callbackWithInfo:(int)someOtherInfo
{
NSLog(#"Some info: %d", someOtherInfo);
}
#end
static void theCallbackFunction(void *context, int someOtherInfo)
{
MyClass *object = (MyClass *)context;
[object _callbackWithInfo:someOtherInfo];
}
If your C callback function does not accept some sort of context info, then:
It's broken and this should be fixed/reported as a bug.
You will need to rely on storing a pointer-to-self at global, static, scope to be used by the C callback function. This will limit the number of instances of MyClass to one!

Get the object which called a method

If I have a call from within a random class like this:
#implementation SomeClass
- (void) classMethodFoo
{
int a = [SomeSingleton sharedInstance].aValue;
}
#end
Inside SomeSingleton sharedInstance, is there a way to get a reference to the object which called this method (without the called passing self as a parameter of course)?
No, information about the caller isn't passed along automatically.
This is why IBAction methods, for instance, have a sender parameter, and why delegate methods often have a parameter that refers to the delegate's object.

ivars when mixing C and Objective-C

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.