【问题标题】:Wrap an Objective-C class in a C struct to mirror class name将 Objective-C 类包装在 C 结构中以镜像类名
【发布时间】:2020-07-06 01:30:00
【问题描述】:

有没有办法将 Objective-C 类包装在 C struct 中,以使 struct 的名称反映该类的名称?理想情况下,我希望模仿使用 extern "C" in C++ as shown here 的行为,而不是必须指向 TestObject 类型的结构的成员,如下面的简单程序所示。

将 Objective-C 类包装到 C 结构中

testobject.m

#ifndef testobject_h
#define testobject_h

#ifdef __OBJC__
@interface TestObject : NSObject
- (void) printHello;
@end
#endif

typedef struct TestObjectC TestObjectC; 
TestObjectC* newTestObject(void);
void TestObject_printHello(TestObjectC *t);

#endif /* testobject_h */

testobject.m

#import <Foundation/Foundation.h>
#import "testobject.h"

@implementation TestObject

- (void)printHello
{
    printf("HELLO!\n");
}

@end

typedef struct TestObject_t
{
    TestObject *testObject;
}TestObjectC;

TestObjectC* newTestObject(void)
{
    TestObjectC *t = malloc(sizeof(TestObjectC));
    t->testObject = [TestObject new];
    return t;
}

void TestObject_printHello(TestObjectC* t)
{
    [t->testObject printHello];
}

ma​​in.c

#include "testobject.h"

int main(int argc, const char * argv[])
{
    TestObjectC *t = newTestObject();
    TestObject_printHello(t);
    return 0;
}

将 C++ 类包装成 C 结构

TestClass.h

#ifndef __TESTCLASS_H
#define __TESTCLASS_H

class TestClass 
{
    public:
        void printHello(void);
};

#endif

TestClass.cc

#include "TestClass.h"
void TestClass::printHello(void) 
{
    printf("HELLO!\n");
}

MyWrapper.h

#ifndef __MYWRAPPER_H
#define __MYWRAPPER_H

#ifdef __cplusplus
extern "C" {
#endif

typedef struct TestClass TestClass;

TestClass* newTestClass();
void TestClass_printHello(TestClass* v);
void deleteTestClass(TestClass* v);

#ifdef __cplusplus
}
#endif
#endif

MyWrapper.cc

#include "TestClass.h"
#include "MyWrapper.h"

extern "C" 
{
  TestClass* newTestClass() 
  {
          return new TestClass();
  }

  void TestClass_int_set(TestClass* v, int i) 
  {
          v->printHello();
  }
  void deleteTestClass(TestClass* v) 
  {
          delete v;
  }
}

C 程序

#include "MyWrapper.h"
#include <stdio.h>

int main(int argc, char* argv[]) 
{
  struct TestClass* c = newTestClass();
  TestClass_printHello(c);        
  deleteTestClass(c);
}

【问题讨论】:

  • 一个 ObjC 对象一个 C 结构。 typedef struct objc_object { Class isa; } *id。这就是 CoreFoundation 的工作方式,允许您透明地将 ObjC 对象传递给 CoreFoundation C 函数。特别注意“免费桥接”。 developer.apple.com/library/archive/documentation/General/…
  • 另外,不要忘记内存管理。您当前的解决方案似乎违反了 ARC 规则,并且您没有在任何地方发送 -release,所以我看不到 TestObject 是如何被释放的。我建议花一些时间在opensource.apple.com/source/objc4/objc4-779.1/runtime,尤其是objc.h。
  • 这个例子有点臃肿,我已经放弃了initdealloc 来获得一些接近简洁的东西。假设这是一个单独的问题,但是是的,ARC 不会处于活动状态
  • 请参阅github.com/iosptl/ios7ptl/blob/master/ch24-DeepObjC/Runtime/… 以获取完全从 C 管理 ObjC 的示例。我不太清楚您在这里尝试使用包装器实现什么。 ObjC 运行时具有完整的 C API。 (myMsgSend 展示了如何手动实现 msg_send,您通常不需要它)
  • @RobNapier 抱歉,在我跳跃之前没有看。 sdk 的入口点只是一个 main.c 文件,我认真地认为可以将其更改为 main.m 以使用 objc 类公开

标签: c objective-c class struct wrapper


【解决方案1】:

这里不需要包装器。 ObjC 对象可以毫无问题地通过 C 进行往返,甚至可以支持 ARC。我将在这里稍微改变一下样式以匹配 CoreFoundation。我将再次编写它以展示它的外观,而不会将调用者暴露给 CoreFoundation。

main.c

int main(int argc, const char * argv[])
{
    // "Ref" means pointer. ...Create() means you must Release it.
    TestObjectRef t = TestObjectCreate();

    // Call your function
    TestObjectPrintHello(t);

    // And release it.
    CFRelease(t);
    return 0;
}

testobject.h

#ifndef testobject_h
#define testobject_h

#import <CoreFoundation/CoreFoundation.h>

// Only define the ObjC part in ObjC
#ifdef __OBJC__
#import <Foundation/Foundation.h>

@interface TestObject : NSObject
- (void) printHello;
@end

#endif

// This is the "void *" of CoreFoundation, but it lets the compiler know this
// this is a bridged ObjC object. This is the standard CF way of naming pointer types.
typedef CFTypeRef TestObjectRef;

// This is the standard CF way of naming functions. "Create" means you need to release it.
TestObjectRef TestObjectCreate(void);

// CF functions always start with their class
void TestObjectPrintHello(TestObjectRef t);

#endif /* testobject_h */

testobject.m

#import "testobject.h"

@implementation TestObject

- (void)printHello
{
    printf("HELLO!\n");
}

@end

// ARC is no problem. We just need to tell ARC that we intend to keep an extra retain.
TestObjectRef TestObjectCreate(void) {
    return CFBridgingRetain([TestObject new]);
}

// And we can get back to ObjC using `__bridge`
void TestObjectPrintHello(TestObjectRef t) {
    [(__bridge TestObject *)t printHello];
}

该解决方案将 CoreFoundation.h 泄漏给调用者,并强制采用 CF 样式命名。我碰巧在我的系统中非常喜欢它。但有时我正在开发现有的跨平台 C 系统,而您想隐藏这些细节。因此,如果我试图匹配您示例中的样式,我会这样做:

main.c

int main(int argc, const char * argv[])
{
    TestObjectC *t = newTestObject();
    TestObject_printHello(t);
    TestObject_release(t); // Still need memory management. Can't avoid that.
    return 0;
}

testobject.h

#ifndef testobject_h
#define testobject_h

#ifdef __OBJC__
#import <Foundation/Foundation.h>
@interface TestObject : NSObject
- (void) printHello;
@end
#endif

typedef const void TestObjectC; // 'const void *' is more CF-like, but either is fine

TestObjectC* newTestObject(void);
void TestObject_printHello(TestObjectC *t);
void TestObject_release(TestObjectC *t);

#endif /* testobject_h */

testobject.m

#import "testobject.h"

@implementation TestObject

- (void)printHello
{
    printf("HELLO!\n");
}

@end

TestObjectC* newTestObject(void) {
    return CFBridgingRetain([TestObject new]);
}

void TestObject_printHello(TestObjectC *t) {
    [(__bridge TestObject *)t printHello];
}
void TestObject_release(TestObjectC *t) {
    CFRelease(t);
}

【讨论】:

  • 更加优雅。这样就可以记录下我不是完全疯了,CFTypeRef只出现在one other c tagged SO question中。
  • 我现在遇到的问题是 将 Objective-C 类包装在 C 结构中以镜像类名 现在这个问题的名称不好,因为我选错了首先接近。
  • 是的,这就是为什么这是一个 xy 问题:D(即使您确实以非常好的方式提出了问题)。我会留下这个标题,因为它可能会帮助未来以同样方式思考它的搜索者。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-26
  • 1970-01-01
  • 1970-01-01
  • 2014-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多