前言:我几乎没有在iOS中进行C集成的经验,请随时纠正我对此的任何误解。
我有一个项目,该项目具有一个用C编写的自定义2层“ SDK” 。coreSDK对deviceSDK进行方法调用,该设备与ios框架进行通信以执行硬件操作(即启用摄像头)或检索设备信息(即。 NSDocumentDirectory的路径)。
不幸的是,由于基础结构的缘故,两个SDK文件扩展名都使用(.h)和(.c),并且不能像某些来源建议的那样更改为(.m)。根据我所读的内容,我可以为ObjC方法创建C回调,但这仅对单例才真正可行,并且C回调需要在(.m)文件的范围内。
我尝试在ObjC中创建包装类,以便它具有deviceSDK调用的C回调,但是当包含该(.m)的标头(.h)时,编译器似乎崩溃了。如果我的理解是正确的,那是因为C编译器无法解释(.m)中包含的ObjC语言。
我认为从理论上讲可以用ObjC运行时用纯C语言编写iOS应用程序,但理想情况下不希望这样做,因为我所见过的事情太复杂了。
工作流程示例
我怎样才能做到这一点?
代码示例将是最全面的,值得赞赏。
谢谢!
这些是我已经研究过的一些(不是全部)参考书...
为了澄清这个问题,我无法从(.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);
}
注意第二个功能;如果需要,您可以在C语言环境中传递对象引用,只要将其隐藏在void *
。中即可。对于内存管理,还需要进行一些强制转换。下面的更多内容。
这是我们将要使用的令人兴奋的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行使这些功能:
// 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
其返回到ObjC土地之前,不会意外释放该对象。
最后一点:如果要在C中大量传递对象引用,则可以考虑typedef void * StringCounterRef;
适当地做和更改所有内容的签名。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句