- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
前言:我对 iOS 中的 C 集成经验不多,请随时纠正我对此的任何误解。
我有一个项目,它有一个自定义的 2 层“SDK”,都是用 C 编写的。coreSDK 对与 通信的 deviceSDK 进行方法调用>ios 框架 执行硬件操作(即启用相机)或检索设备信息(即 NSDocumentDirectory 的路径)。
不幸的是,由于基础设施的原因,两个 SDK 文件扩展名都使用 (.h) 和 (.c),并且不能像某些来源推荐的那样更改为 (.m)。根据我读到的内容,我可以创建 ObjC 方法的 C 回调,但这只对单例真正可行,并且 C 回调需要在 (.m) 文件的范围内。
我尝试在 ObjC 中创建一个包装器类,这样它就有一个 C 回调,deviceSDK 从中调用,但是当包含该 (.m) 的 header (.h) 时,编译器似乎崩溃了。如果我的理解是正确的,那是因为 C 编译器无法解释 (.m) 中包含的 ObjC 语言。
我相信理论上可以使用 ObjC 运行时用纯 C 语言编写 iOS 应用程序,但理想情况下我宁愿不要走那条路,因为我所看到的非常复杂。
示例工作流
我怎样才能做到这一点?
代码示例将是最全面和最受赞赏的。
谢谢!
这些是我已经研究过的一些(不是全部)引用资料......
为了澄清这个问题,我无法从 (.c) 文件中的 C 方法调用到 (.m) 文件中的 ObjC 方法,或者想出一种替代方法来检索信息,例如(例如)NSDocumentsDirectory在 ObjC(.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 语法的 header 。您的 C 代码中包含的 ObjC 端的任何接口(interface)都需要分解为仅包含有效 C 的自己的 header 。这是一个您需要的演示:
首先,C-only 的头文件和实现文件。
// 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 函数的头文件。 header 本身不包含 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
的接口(interface),因此函数可以使用该类的方法:
// 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 中或任何您喜欢的地方,您都可以从 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;
并适本地更改所有内容的签名。
关于ios - 如何从纯 C(.c 文件)SDK 到 IOS(obj-C,.m 文件)框架进行通信?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38489504/
我是一名优秀的程序员,十分优秀!