如何从纯C(.C文件)SDK到IOS(obj-C,.m文件)框架进行通信?
前言:我对iOS中的C集成几乎没有经验,请随时纠正我对此的任何误解 我有一个项目,它有一个自定义的两层“SDK”,都是用C编写的。coreSDK对deviceSDK进行方法调用,deviceSDK与ios框架进行通信,以执行硬件操作(即启用摄像头)或检索设备信息(即NSDocumentDirectory的路径) 不幸的是,由于基础设施的原因,SDK文件扩展名都使用(.h)和(.c),并且不能像某些源建议的那样更改为(.m)。根据我读到的内容,我可以创建ObjC方法的C回调,但这只适用于单例,并且C回调需要在(.m)文件的范围内 我曾尝试在ObjC中创建一个包装类,这样它就有一个C回调,deviceSDK从中调用,但当包含该(.m)的头(.h)时,编译器似乎崩溃了。如果我的理解是正确的,那是因为C编译器无法解释(.m)中包含的ObjC语言 我相信从理论上讲,用纯C和ObjC运行时编写iOS应用程序是可能的,但在理想情况下,我宁愿不走这条路,因为我所看到的是荒谬的复杂 工作流示例如何从纯C(.C文件)SDK到IOS(obj-C,.m文件)框架进行通信?,ios,objective-c,c,iphone,Ios,Objective C,C,Iphone,前言:我对iOS中的C集成几乎没有经验,请随时纠正我对此的任何误解 我有一个项目,它有一个自定义的两层“SDK”,都是用C编写的。coreSDK对deviceSDK进行方法调用,deviceSDK与ios框架进行通信,以执行硬件操作(即启用摄像头)或检索设备信息(即NSDocumentDirectory的路径) 不幸的是,由于基础设施的原因,SDK文件扩展名都使用(.h)和(.c),并且不能像某些源建议的那样更改为(.m)。根据我读到的内容,我可以创建ObjC方法的C回调,但这只适用于单例,并且
代码示例将是最全面和最受欢迎的。
谢谢
这些是我已经研究过的一些(不是全部)参考资料
编辑:2016-07-21 为了澄清这个问题,我在从(.C)文件中的C方法调用(.m)文件中的ObjC方法时遇到了问题,或者在寻找另一种方法来检索信息时遇到了问题,例如,在ObjC(.m)文件中检索NSDocumentsDirectory并将其传回(.C)文件中的C 示例代码:当然这是不正确的,但它符合我的期望或我希望实现的目标
//GPS.h
#include "GPSWrapper.h"
STATUS_Code GPSInitialize(void);
这一行#在您的示例中包括“GPSWrapper.h”
就是问题所在。在编译为C的文件中,不能包含具有ObjC语法的头文件。C代码中包含的ObjC端的任何接口都需要拆分为只包含有效C的头文件。下面是您需要的演示:
首先,头文件和实现文件,用于C-only文件
// CUtils.h
// OCFromC
#ifndef CUtils_h
#define CUtils_h
#include <stdio.h>
void doThatThingYouDo();
void doThatThingWithThisObject(const void * objRef);
#endif /* CUtils_h */
注意第二个函数;如果需要,可以在C语言中传递对象引用,只要它被隐藏在void*
中。内存管理还需要进行一些转换。下面将详细介绍
这是我们将使用的激动人心的ObjC类:
// StringCounter.h
// OCFromC
#import <Foundation/Foundation.h>
@interface StringCounter : NSObject
- (size_t)lengthOfBytesInUTF32:(const char *)s;
@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 */
现在,无论是在主要场合还是在任何你喜欢的场合,你都可以从表皮上锻炼这些功能:
// 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;
}
//main.m
//OCFromC
#进口
#导入“StringCounter.h”
#导入“CUtils.h”
int main(int argc,const char*argv[]{
@自动释放池{
doThatThing();
//创建对象引用
StringCounter*sc=[StringCounter新建];
//告诉ARC我知道这很危险相信我,
//把这个参考资料传下去。
const void*scRef=(uu桥u保留的void*)sc;
使用此对象进行任何操作(scRef);
}
返回0;
}
桥接投射与StringCounterCInterface.m中的对应项一起,允许您在圆弧无法到达的区域移动ObjC对象。它会在对象隐藏在void*
中之前碰撞对象的保留计数,以便在您可以\uuuu bridge\u将其传输回ObjC land之前不会意外释放它
最后一个注释:如果你要通过C中的对象引用很多,你可以考虑做<代码> TyPulfValue* StringCounterRef;<代码>并适当地更改所有内容的签名。
OSX和iOS音频程序充满了有益的示例:虽然整个程序都是用Obj-C编写的,但关键的音频处理代码必须“精简”到纯C。请参阅所谓的“核心音频”标签。这没有真正意义;从ObjC中,C是100%可用的。是的,这是单向的:您不能在编译为C的文件中包含ObjC头,也不能发送ObjC消息,但在ObjC类或编译为ObjC的文件中的C函数中为您的必选项C函数提供包装应该没有问题。你能不能提供一个你正在做的事情的最小演示,并准确地解释它是如何失败的?你可以从Objective-C调用C方法,这没什么大不了的。不要重命名,只需包含.c和.h文件,然后从那里开始。问题不在于从ObjC调用C,而在于从(.C)文件中的C方法调用(.m)文件中的ObjC。我用额外的细节和测试代码样本更新了我的问题,或者我的期望值是什么。这是我问题的一个漂亮答案。虽然有一个稍微相关的问题,但我注意到这里我们返回的是字节大小的size\u t,但在处理a时,我必须将objc中字符串的返回类型定义为const char*
to c(知道c字符串是字符数组)。我花了一段时间才弄明白这一点,但是在这个场景中我如何定义其他常见的数据类型呢?我选择了size\u t
,这样C端就不必得到NSUInteger
的定义,这是lengthofBytes susingCodeing:
的实际返回类型。严格说来,它们不是一样的,但实际上是一样大小的
// 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);
}
// 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
// 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 */
// 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];
}
// 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;
}