如何从纯C(.c文件)SDK到IOS(obj-C,.m文件)框架进行通信?

前言:我在iOS中没有C语言集成的经验,随时纠正我对这个问题的任何误解。

我有一个项目,有一个自定义的2层“SDK”都用C编写。coreSDK方法调用deviceSDKios框架进行通信,执行硬件操作(即启用相机)或检索设备信息(即。pathNSDocumentDirectory)。

不幸的是,由于基础设施的原因,两个SDK文件扩展名都使用(.h)和(.c),并且不能像某些源文件所推荐的那样将其改为(.m)。 根据我读过的内容,我可以为ObjC方法创buildCcallback函数,但这对于单例实际上是唯一可行的,并且Ccallback函数需要位于(.m)文件的范围内。

我已经尝试在ObjC中创build一个包装类,使它具有deviceSDK调用的Ccallback函数 ,但是当包含该头文件(.h)时,编译器似乎崩溃了。 如果我的理解是正确的,那是因为C编译器不能解释(.m)中包含的ObjC语言。

我相信在理论上可以使用ObjC运行时在纯C中编写iOS应用程序,但理想情况下,不应该沿着这条路线走,因为我所看到的很荒谬。

示例工作stream程

  1. ViewController.m方法调用coreSDK方法(.c)
  2. coreSDK方法调用deviceSDK方法(.c)
  3. deviceSDK需要检索(例如) NSDocumentDirectory (或启用摄像头)

我怎样才能做到这一点?
代码示例将是最全面的和赞赏。
谢谢!


这些是我已经看过的一些(不是全部)引用…

  • 如何纯粹写在iOS中的iOS应用程序
  • 纯C函数调用Objective-C方法
  • C函数调用目标C函数
  • 使用本机Objective-C方法进行Ccallback
  • 从C到Objective-C的callback方法
  • 在Objective-C类中混合使用C函数
  • 如何从C方法调用Objective-C方法
  • 如何在一个Objective-C项目中使用纯C文件

编辑:2016-07-21

为了澄清这个问题,我很难从(.c)文件中的C方法调用到(.m)文件中的ObjC方法,或者找出一种替代方法来检索信息,例如(ObjC)中的NSDocumentsDirectory .m文件)并传回C(.c)文件。

示例代码:当然这是不正确的,但是它符合我的期望或我希望达到的目标。

//GPS.h #include "GPSWrapper.h" STATUS_Code GPSInitialize(void); 
 //GPS.c #include "GPS.h" STATUS_Code GPSInitialize(void) { GPS_cCallback(); } 
 //GPSWrapper.h #import <Foundation/Foundation.h> @interface GPSWrapper: NSObject - (void) testObjC; @end 
 //GPSWrapper.m #import "GPSWrapper.h" static id refToSelf = nil; @implementation GPSWrapper - (void) testObjC { // just an example of a ObjC method call NSString *documentsDirectory = [paths objectAtIndex:0]; NSLog(documentsDirectory); } @end static void GPS_cCallback() { [refToSelf testObjC]; } 

这行#include "GPSWrapper.h"在你的例子是问题。 你不能在一个被编译为C的文件中包含一个带有ObjC语法的头文件。任何包含在你的C代码中的ObjC端的接口都需要被分解到它自己的只包含有效的C的头文件中。演示你需要什么:

首先,C头文件的头文件和实现文件。

 // CUtils.h // OCFromC #ifndef CUtils_h #define CUtils_h #include <stdio.h> void doThatThingYouDo(); void doThatThingWithThisObject(const void * objRef); #endif /* CUtils_h */ 

 // CUtils.c // OCFromC // Proof that this is being compiled without ObjC #ifdef __OBJC__ #error "Compile this as C, please." #endif #include "CUtils.h" #include "StringCounterCInterface.h" void doThatThingYouDo() { printf("%zu\n", numBytesInUTF32("Calliope")); } void doThatThingWithThisObject(const void * objRef) { size_t len = numBytesInUTF32WithRef(objRef, "Calliope"); printf("%zu\n", len); } 

请注意,第二个function; 如果你需要的话,只要在void *隐藏一个对象,你就可以绕过C对象的引用。 还有一些需要做内存pipe理的工作。 更多的在下面。

这是我们将使用的令人兴奋的ObjC类:

 // StringCounter.h // OCFromC #import <Foundation/Foundation.h> @interface StringCounter : NSObject - (size_t)lengthOfBytesInUTF32:(const char *)s; @end 

  // StringCounter.m // OCFromC #import "StringCounter.h" @implementation StringCounter - (size_t)lengthOfBytesInUTF32:(const char *)s { NSString * string = [NSString stringWithUTF8String:s]; return [string lengthOfBytesUsingEncoding:NSUTF32StringEncoding]; } @end 

现在,这是重要的一部分。 这是一个声明C函数的头文件。 头本身不包含ObjC,因此可以像上面所看到的那样包含在CUtils.c中。

 // StringCounterCInterface.h // OCFromC #ifndef StringCounterCInterface_h #define StringCounterCInterface_h // Get size_t definition #import <stddef.h> size_t numBytesInUTF32(const char * s); size_t numBytesInUTF32WithRef(const void * scRef, const char *s); #endif /* StringCounterCInterface_h */ 

这是连接点。 它的实现文件被编译为ObjC,它包含刚刚声明的那些函数的定义。 这个文件导入了StringCounter的接口,所以函数可以使用这个类的方法:

 // StringCounterCInterface.m // OCFromC #ifndef __OBJC__ #error "Must compile as ObjC" #endif #import "StringCounterCInterface.h" #import "StringCounter.h" size_t numBytesInUTF32(const char * s) { StringCounter * sc = [StringCounter new]; // Or, use some "default" object in static storage here return [sc lengthOfBytesInUTF32:s]; } size_t numBytesInUTF32WithRef(const void * objRef, const char * s) { StringCounter * sc = (__bridge_transfer StringCounter *)objRef; return [sc lengthOfBytesInUTF32:s]; } 

现在,在主要或任何你喜欢的地方,你可以从CUtils中运行这些function:

 // main.m // OCFromC #import <Foundation/Foundation.h> #import "StringCounter.h" #import "CUtils.h" int main(int argc, const char * argv[]) { @autoreleasepool { doThatThing(); // Create an object reference StringCounter * sc = [StringCounter new]; // Tell ARC that I know this is dangerous, trust me, // pass this reference along. const void * scRef = (__bridge_retained void *)sc; doThatThingWithThisObject(scRef); } return 0; } 

桥接转换,以及在StringCounterCInterface.m中的对应部分,可让您将ObjC对象移动到ARC无法运行的区域。 它将对象的保留计数在隐藏在void *之前__bridge_transfer过来,这样在将__bridge_transfer传回ObjC之前,它将不会被意外释放。

最后一点:如果你要在C中传递很多对象引用,你可以考虑使用typedef void * StringCounterRef; 并适当地改变一切的签名。