在 iOS/MacOS 中调用本机函数时,如何将复杂的 swift 数据类型传递给 C 数据类型?

How can I pass complex swift datatypes to C datatypes when calling native functions in iOS/MacOS?

我设法获得了一个包含在框架中的基本 dylib,它允许我传入 Int 并返回和 Int 工作,但我将如何传递和返回更复杂的数据类型,如指针、字节数组或实际数据结构快速到 C dylib?

是否有任何教程或资源可以将 swift 数据类型映射/传递/转换为 C 数据类型,反之亦然,就像在 JNI for java 中一样?


在 Swift 端解释 C 结构并从 c-dylib 动态提取函数的示例如下所示:

.c 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
Person *get_person() {
    Person *p = malloc(sizeof(Person));
    p->first_name = strdup("Branford");
    p->last_name = strdup("Marsalis");
    p->age = 60;
    return p;
}

void free_person(Person *person) {
    free(person->first_name);
    free(person->last_name);
    free(person);
}

.h 文件

1
2
3
4
5
typedef struct {
    char *first_name;
    char *last_name;
    int age;
} Person;

斯威夫特

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
typealias getPersonFunc = @convention(c) () -> UnsafeMutablePointer<Person>
typealias freePersonFunc = @convention(c) (UnsafeMutablePointer<Person>) -> Void

...

let handle = dlopen("libsimple.dylib", RTLD_LOCAL|RTLD_NOW)
let get_person_sym = dlsym(handle,"get_person")
let getPerson = unsafeBitCast(get_person_sym, to: getPersonFunc.self)
let cPerson = getPerson()
let person = cPerson.withMemoryRebound(to: Person.self, capacity: 1) { $0.pointee }
let firstName = String(cString: UnsafeRawPointer(person.first_name).assumingMemoryBound(to: CChar.self))
let lastName = String(cString: UnsafeRawPointer(person.last_name).assumingMemoryBound(to: CChar.self))

print(firstName)
print(lastName)
print(person.age)

let free_person_sym = dlsym(handle,"free_person")
let freePerson = unsafeBitCast(free_person_sym, to: freePersonFunc.self)
freePerson(cPerson)

dlclose(handle)

测试

此示例在调试控制台上的输出如下所示:

1
2
3
Branford
Marsalis
60

从 Swift 到 C

假设在 .c:

1
2
3
4
5
6
7
void print_person(Person *person) {
    printf("%s %s is %d years old\
"
,
           person->first_name,
           person->last_name,
           person->age);
}

然后在 Swift 方面可以这样写:

1
2
3
4
5
6
7
8
9
10
typealias printPersonFunc = @convention(c) (UnsafeMutablePointer<Person>) -> Void
...
let newPerson = UnsafeMutablePointer<Person>.allocate(capacity: 1)
newPerson.pointee.first_name = UnsafeMutablePointer<Int8>(mutating: ("Norah" as NSString).utf8String)
newPerson.pointee.last_name = UnsafeMutablePointer<Int8>(mutating: ("Jones" as NSString).utf8String)
newPerson.pointee.age = 41
let print_person_sym = dlsym(handle,"print_person")
let printPerson = unsafeBitCast(print_person_sym, to: printPersonFunc.self)
printPerson(newPerson)
newPerson.deallocate()

这将在控制台上给出以下输出:

1
Norah Jones is 41 years old