How to call Objective-C "self" from C method [duplicate] - ios

This question already has answers here:
Pass instance method as function pointer to C Library
(1 answer)
How do I give C function a pointer to 'self' (calling obj) in objective-c?
(2 answers)
Closed 9 years ago.
I have Objective-C class in my iOS project that implements Objective-C and C code in the same class. I changed the extension to .mm and this part goes well. Now I want to set a C method that will call the Objective-C method in the same class. The problem I get is when I am trying to call self from the C method.
Here is the code :
void SetNotificationListeners(){
[self fireSpeechRecognition];
}
the error is :
Use of undeclared identifier 'self'
how can I manage this?

You have to pass the instance pointer to the C function:
void SetNotificationListeners(void *uData)
{
MyClass *obj = (__bridge MyClass *)(uData);
[obj fireSpeechRecognition];
}
- (void)myMethod
{
// Call C function from Objective-C method:
myFunction((__bridge void *)(self));
}
(The "brigde" stuff is needed only if you compile with ARC.)

Either give self as an argument to the function call:
void SetNotificationListeners(void *myObj){
[(MyClass*)myObj fireSpeechRecognition];
}
//in objC
SetNotificationListeners(self);
or have a global variable that holds reference
//global variable
static MyClass *myObj;
//in obj c
myObj = self;
SetNotificationListeners(); //use myObj instead of self in function
The first is better in my opinion.

You don’t have to change the file extension. Objective-C is a superset of C, which means you can use plain C in your Objective-C files as you please.
When you write an implementation of an Objective-C method, that method always executes in context of some particular instance, that’s the self part. You get the self in an Objective-C method automagically, but behind the scenes it’s just passed as an argument to the method, see obc_msgsend.
Your plain C function is not a part of the class (plain C functions never are), therefore there’s no instance associated with it when you call it, there’s no implicit self. If you want the function to call some instance, you have to pass the pointer to that instance explicitly. For example some of the plain C APIs have a “context” pointer you can pass when registering for a callback. See the neighboring answers for examples of this.

In Objective-C methods there are two parameters that are implicitly passed into each method, which are self and _cmd. This is why you can access self from within a method.
You could just pass self as an argument to your c function when calling from an Objective-C method, but with that trivial example I'm not sure why you wouldn't just use a method.
You do not need to change the file extension to .mm unless you are using Objective-C++

Related

Cannot use all methods in Objective-C class in Swift

I am trying to make use of an Objective-C API in Swift. I can only seem to call the shareMyInstance() method from Swift, and not the initOpenApi() method for some reason. I'm not sure if there is some sort of scope identifier present in the interface, but I can't make use of initOpenApi, even though both are in the header. I also cannot see the method bodies, but I don't believe that affects the scope of the function.
This is locking me into using Objective-C, because for some reason I can access all of the functions from Objective-C, but only 3 of the 4 from Swift.
Header file (LCOpenSDK_Api.h):
#ifndef LCOpenSDK_LCOpenSDK_Api_h
#define LCOpenSDK_LCOpenSDK_Api_h
#import <Foundation/Foundation.h>
#interface LCOpenSDK_Api: NSObject
+ (LCOpenSDK_Api*) shareMyInstance;
- (id) initOpenApi:(NSString*)addr port:(NSInteger)port CA_PATH:(NSString*)caPath;
- (NSInteger)request:(void*)req resp:(void*)resp timeout:(NSInteger)timeout;
- (void)uninitOpenApi;
#end
#endif
My code (.swift):
import Foundation
#objc(LeChangePlayerView)
class LeChangePlayerView: UIView {
//...
#objc
override init(frame: CGRect) {
super.init(frame: frame)
var lc = LCOpenSDK_Api.shareMyInstance()!; //Fine
//Need this function!
lc.initOpenApi("openapi.easy4ip.com", 443, "") //Value of type 'LCOpenSDK_Api' has no member 'initOpenApi'
}
The only possible other explanation is that there is a different header file with the same name, but different interface, but this is highly unlikely because shareMyInstance, request and unitOpenApi are all available, and going to the definition from within the swift file using Xcode points to the same file. It is a dynamic framework, and as of right now, I can only view the headers, not the method implementations. I'm not sure if there's a solution to this, but this is another problem I could use help with. Could they have locked the original source code somehow, as well as made that specific method private?
Although initOpenApi is an instance method, Swift recognises it as an initialiser as it starts with the word init. Initialisers are translated from Objective-C into Swift-style initialisers.
In Objective-C you would say something like [[LCOpenSDK_Api alloc] initOpenAPI:#"openapi.easy4ip.com", port: 443, CA_PATH: #""]
In Swift the word init is stripped and there is no need to explicitly allocate a new instance:
let lc = LC_OpenSDK_Api(openApi:"openapi.easy4ip.com:, port: 443, CA_PATH:"")
However, you need to refer to the documentation from the framework to determine if you want to access the singleton instance LC_OpenSDK_Api.shareMyInstance or whether you want to create a specific instance as I showed above.

How to bridge Objective-C initWithError: method into Swift

I have a class defined in Objective-C, whose initializer is -initWithError: (the initializer can fail due to a dependence on an outside resource). I want this to bridge into Swift as init() throws. The regular initializer inherited from NSObject, -init, can be marked unavailable, as I don't want it to be used.
In Objective-C, I have:
#interface Foo : NSObject
- (instancetype _Nullable)initWithError:(NSError **)error;
#end
This works fine in Objective-C, of course.
In Swift, -initWithError gets bridged as init(error: ()) throws. This is presumably because removing withError: from the method name results in init() which conflicts with the inherited plain -init initializer. This can be called from Swift like so:
let foo = try Foo(error: ())
This is strange looking, as the error parameter is void. It would certainly be better if this were imported as init() throws. The obvious solution is to mark -init using NS_UNAVAILABLE in the Objective-C header. Unfortunately, this doesn't work. -initWithError: still gets bridged as init(error: ()), and trying to call try Foo() results in a compiler error saying that init() is unavailable in Swift.
Is there a more elegant solution to this so that try init() just works?
You can rename the function using NS_SWIFT_NAME. In this case:
- (instancetype _Nullable)initWithError:(NSError **)error NS_SWIFT_NAME(init());
That said, this feels like a compiler bug. I'd suggest opening a defect.

Objective C++: Call Objective C method from C++

I'm using NSURLSession to connect to a database. I have this already implemented in C++ for Windows and am trying to get it working on iOS also. I have a .h file derived from a base C++ class that is the header for my .mm file. If I'm correct I have to implement all the functions in my .h file in C++. However NSURLSession is an Objective-C function. How do I call an Objective-C method from my C++ function?
I have a C++ function called Connect() where I make a C++ object m_Delegate that has an alloc and init.
this->m_Delegate = [[PrivateNSURLSessionDelegate alloc] initWihParent:this];
//where PrivateNSURLSessionDelegate is the name of my interface.
That interface has -(bool)NSConnect (with implementation in the #implementation) which I'm trying to call from:
void Connect()
{
[PrivateNSURLSessionDelegate NSConnect];
//This however gives me the error: +[PrivateNSRLSessionDelegate NSConnect]: unrecongnized selector sent to class
}
I also tried it using my C++ object
void Connect()
{
[m_Delegate NSConnect];
//This gives me a error that is unrecognized selector sent to instance
}
Is there a better way to do this? I basically want to ask the Objective-C to do all the NSURL stuff and send just the data back to the C++.
I'm completely new to Objective-C so any and all help would be greatly appreciated! Thanks!
-(bool)NSConnect
Here the - indicates it is an instance method. Conversely + would indicate a class method.
That being said, [PrivateNSURLSessionDelegate NSConnect]; calls a class method, since you call it on the interface PrivateNSURLSessionDelegate.
However, this is not defined as it is defined as NSConnect is defined as an instance method (btw the convention is that (instance) methods always start with a lowercase).
[m_Delegate NSConnect];
Does however call the instance method. You should define -(bool)NSConnect in the header file of PrivateNSURLSessionDelegate, not above the #implementation in the implementation file, that makes in a private method and thus inaccessible.
There is Objective-C, which is a superset of C, and Objective-C++, which is a superset of C++. Objective-C++ source code files have a .mm suffix, where Objective-C would have a .m suffix.
You cannot call Objective-C from C++. You can however call Objective-C from Objective-C++, and you can write your usual C++ classes in Objective-C++ as well.

use of self keyin Objective C

I am new in IOS programming.I have learnt in C++ that, 'this pointer' is used and can be accessed only inside and only for instance function(object method as in IOS ).we can't access ''this pointer'' inside the Class functions(class method in IOS ).but I have checked that I can also use them(as a self ) inside the class method in IOS. but I don't know ,May be my concept wrong or not. I have searched many sites. I have checked that,many places it is used..If 'self' can be used inside the class method ,So anyone can please tell me, How it can possible?. Is the concept of 'self' is different as 'this pointer' in C++.please explain it step by step..Thanks in advance
#import" ViewController.h"
#implementation ViewController
+(void)fun
{
[self action]; //here you can see ,Iam using 'self' for access method action.
}// but according to C++ this pointer (self)can't be used in class method.
// not giving any error...please check.
+(void)action
{
NSLog(#" Welcome in IOs world");
}
-(void)viewDidLoad{
[superviewDidLoad];
ViewController*obj=[[ViewController alloc]init];
[ViewController fun];
}
#end
In a class method self refers to the class, and in an instance method self refers to the instance of the class. So the same terminology can be used but means different things in different scopes.

Dynamically implementing a delegate during runtime

In my class, I have a reference on an UIViewController and want to implement a delegate on this ViewController during runtime. The delegate has only one method (with two parameters) and when the delegate-method on the ViewController is invoked, my class should handle the call.
I am quite sure this is possible with some kind of method swizzling, etc. but I don't know how to accomplish this.
What you want is possible, but it's not method swizzling, since you don't want to switch to methods but add a new one. It can be done, thanks to Objective-C's dynamic nature, but it's still a dirty hack so also file a feature request with the library vendor.
What you want is class_addMethod() and a C function with the actual implementation for that. One more thing, Objective-C methods are C methods, but with two implicit parameters, self and _cmd, which have to keep in mind (both when creating your C method and when telling class_addMethod your methods signature. And here is an SSCE of how to pull something like that off:
#import <Foundation/Foundation.h>
#import <objc/runtime.h> // Required for class_addMethod()
#interface MyClass : NSObject
#end
#implementation MyClass
#end
#protocol MyProtocol <NSObject>
- (void)printString:(NSString *)string;
#end
// Note the method signature containing the
// two implicit parameters self and _cmd!
void MyClassPrinStringIMP(id self, SEL _cmd, NSString *string)
{
NSLog(#"Hi I'm %#:%s and this is the string: %#", self, sel_getName(_cmd), string);
}
void PimpMyClass()
{
// The last argument is the signature. First character is the return type, in our case void
// Then comes self and _cmd, followed by the NSString. You can use #encode() to find out how your
// type is encoded. Best is to build this string at runtime, since the encoding can change with architectures
class_addMethod([MyClass class], #selector(printString:), (IMP)MyClassPrinStringIMP, "v#:#");
}
int main(int argc, const char * argv[])
{
#autoreleasepool
{
PimpMyClass();
id foo = [[MyClass alloc] init]; // id, to silence the compiler!
[foo printString:#"Hello World"];
}
return 0;
}
Example output:
Hi I'm <MyClass: 0x100101810>:printString: and this is the string: Hello World
Edit: Something that you may find is that the passed object is checked at runtime wether it conforms to a protocol or not using conformsToProtocol:. Since this code just adds the method implementation, it would still fail, but you can tell the runtime that you totally do implement that protocol with this one function call:
class_addProtocol([MyClass class], #protocol(MyProtocol));
Alternative: proxies
Objective-Cs dynamism and message forwarding is already praised by #JasperBlues, however, there is one particular class in Objective-C that is designed to do just that: NSProxy. It is designed to intercept sent messages and dispatching them dynamically to the relevant target, and does use the high-level NSInvocation approach. If you can pass a proxied object in some way as the delegate (depending on what your code allows for and what not), creating a NSProxy subclass might be the cleanest way to go.
However, note though that you then end up with a shim object that wraps over your other object, which comes with its own bag of pain and will break when you try to directly access variables via -> syntax. It's not a perfectly invisible proxy, but good enough for most cases.
Firstly, some comments indicate that what you're asking is instantly "a bad thing to do" or a "dirty hack". I disagree here. Most modern Object Oriented languages support these features, and they are used to good effect by numerous system-level frameworks. Of course it is human-nature to perhaps use these dynamic features where they're not really required (for fun or practice), even when a simpler approach would work fine. Beware of this.
Objective-C is admirable in that its somewhat of a legacy language and close to the "bare metal", and yet features a surprising level of dynamism, making it relatively easy to support these requirements without any external libraries or frameworks.
Besides using the class_addMethod guide that another answer correctly indicates, some other approaches are:
Message Forwarding: (recommended)
All NSObject sub-classes have the ability to forward a method that they're not able to respond to, to another target object. This is similar to the lower-level concept of trampolines. Apple publishes a guide on using this approach.
The advantages of using forward invocation is that it uses the NSInvocation level of abstraction, instead of directly calling the C ObjC runtime API. This abstracts the following details away:
Structs and primitives will be box/unboxed automatically
Dispatching to methods with a dynamic/unknown number of arguments becomes easy. Until arm64, this could be done using va_args, however on arm64 va_args can be copied directly to registers, and not popped off the stack.
Resolve Instance Method:
Instance methods are created by by registering a C function as the implementation to respond to a given message. This can be done neatly with blocks, using IMP_ImplementationWithBlock:
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
IMP imp = imp_implementationWithBlock((__bridge id) objc_unretainedPointer(
^(id me, BOOL firstParam, NSString* secondParam)
{
//Implementation goes in here
return something; //something of type 'id'
}));
class_addMethod(self, sel, imp, "##:");
return YES;
}
return NO;
}
Using libffi:
Libffi can also do this kind of thing, though it should not be necessary if you're using pure Objective-C, as the runtime already has these features baked in.

Resources