7.3.1.1 与 Swift/Objective-C 互操作

原文链接: https://kotlinlang.org/docs/native-objc-interop.html

7.3.1.1 与 Swift/Objective-C 互操作

注意: Objective-C 库导入处于 Beta 阶段。由 cinterop 工具从 Objective-C 库生成的所有 Kotlin 声明都应带有 @ExperimentalForeignApi 注解。随 Kotlin/Native 提供的原生平台库(例如 Foundation、UIKit 和 POSIX)只对部分 API 要求选择启用。

Kotlin/Native 通过 Objective-C 提供与 Swift 的间接互操作。本文介绍如何在 Swift/Objective-C 代码中使用 Kotlin 声明,以及如何在 Kotlin 代码中使用 Objective-C 声明。

你可能还会觉得以下资源有用:

把 Swift/Objective-C 库导入 Kotlin

Objective-C 框架和库如果被正确导入到构建中(系统框架默认导入),就可以在 Kotlin 代码中使用。更多细节请参阅:

如果 Swift 库的 API 通过 @objc 导出到 Objective-C,就可以在 Kotlin 代码中使用。纯 Swift 模块目前尚不受支持。

在 Swift/Objective-C 中使用 Kotlin

如果 Kotlin 模块被编译为 framework,就可以在 Swift/Objective-C 代码中使用:

对 Objective-C 和 Swift 隐藏 Kotlin 声明

实验性

为了让你的 Kotlin 代码对 Swift/Objective-C 更友好,请使用 @HiddenFromObjC 注解把某个 Kotlin 声明对 Objective-C 和 Swift 隐藏。它会禁用该函数或属性向 Objective-C 的导出。

或者,你可以用 internal 修饰符标记 Kotlin 声明,以限制它们在编译模块中的可见性。如果你希望把该 Kotlin 声明对 Objective-C 和 Swift 隐藏,同时仍对其他 Kotlin 模块可见,请使用 @HiddenFromObjC。

在 Kotlin-Swift interopedia 中查看示例。

在 Swift 中使用 refinement

实验性

@ShouldRefineInSwift 有助于用 Swift 编写的包装器替换 Kotlin 声明。该注解会把函数或属性在生成的 Objective-C API 中标记为 swift_private。这类声明会带上 __ 前缀,从而对 Swift 不可见。

你仍然可以在 Swift 代码中使用这些声明来创建对 Swift 友好的 API,但它们不会出现在 Xcode 的自动补全建议中。

修改声明名称

实验性

要避免重命名 Kotlin 声明,请使用 @ObjCName 注解。它指示 Kotlin 编译器为带注解的类、接口或其他 Kotlin 实体使用自定义的 Objective-C 和 Swift 名称:

1
2
3
4
5
6
7
8
9
@ObjCName(swiftName = "MySwiftArray")
class MyKotlinArray {
    @ObjCName("index")
    fun indexOf(@ObjCName("of") element: String): Int = TODO()
}

// 使用 ObjCName 注解的用法
let array = MySwiftArray()
let index = array.index(of: "element")

在 Kotlin-Swift interopedia 中查看另一个示例。

使用 KDoc 注释提供文档

文档对于理解任何 API 都至关重要。为共享的 Kotlin API 提供文档,可以让你就用法、注意事项等与使用者沟通。

生成 Objective-C 头文件时,Kotlin 代码中的 KDoc 注释会被转换为相应的 Objective-C 注释。例如,以下带 KDoc 的 Kotlin 代码:

1
2
3
4
5
/**
 * 打印各参数之和。
 * 正确处理了和不适合 32 位整数的情况。
 */
fun printSum(a: Int, b: Int) = println(a.toLong() + b)

会生成带相应注释的 Objective-C 头文件:

1
2
3
4
5
/**
 * 打印各参数之和。
 * 正确处理了和不适合 32 位整数的情况。
 */
+ (void)printSumA:(int32_t)a b:(int32_t)b __attribute__((swift_name("printSum(a:b:)")));

KDoc 注释会被嵌入 klib,并在生成 Apple framework 时从 klib 中提取出来。因此,类和方法的注释会出现在自动补全中,例如在 Xcode 中。如果你跳到 .h 文件中函数的定义处,会看到关于 @param、@return 及类似标签的注释。

已知限制:

  • 除非使用 -Xexport-kdoc 选项编译,否则依赖项的文档不会被导出。使用该编译器选项编译的库可能与其他编译器版本不兼容。
  • KDoc 注释大多按原样导出,但许多 KDoc 块标签(例如 @property)不受支持。

如有必要,你可以在 Gradle 构建文件的 binaries {} 块中禁止把 KDoc 注释从 klib 导出到生成的 Apple framework:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// build.gradle.kts
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi

kotlin {
    iosArm64 {
        binaries {
            framework {
                baseName = "sdk"
                @OptIn(ExperimentalKotlinGradlePluginApi::class)
                exportKdoc.set(false)
            }
        }
    }
}

映射

下表展示了 Kotlin 概念如何映射到 Swift/Objective-C,以及反向的映射。

“->” 和 “<-” 表示映射只朝一个方向进行。

| Kotlin | Swift | Objective-C | 说明 |

| class | class | @interface | 说明 | | interface | protocol | @protocol | | | constructor/create | Initializer | Initializer | 说明 | | 属性 | 属性 | 属性 | 说明 1、说明 2 | | 方法 | 方法 | 方法 | 说明 1、说明 2 | | enum class | class | @interface | 说明 | | suspend -> | completionHandler:/ async | completionHandler: | 说明 1、说明 2 | | @Throws fun | throws | error:(NSError**)error | 说明 | | 扩展 | 扩展 | Category 成员 | 说明 | | companion 成员 <- | 类方法或属性 | 类方法或属性 | | | null | nil | nil | | | Singleton | shared 或 companion 属性 | shared 或 companion 属性 | 说明 | | 原始类型 | 原始类型 / NSNumber | | 说明 | | Unit 返回类型 | Void | void | | | String | String | NSString | 说明 | | String | NSMutableString | NSMutableString | 说明 | | List | Array | NSArray | | | MutableList | NSMutableArray | NSMutableArray | | | Set | Set | NSSet | | | MutableSet | NSMutableSet | NSMutableSet | 说明 | | Map | Dictionary | NSDictionary | | | MutableMap | NSMutableDictionary | NSMutableDictionary | 说明 | | 函数类型 | 函数类型 | Block 指针类型 | 说明 | | 内联类 | 不支持 | 不支持 | 说明 |

类

名称转换

Objective-C 类以原始名称导入 Kotlin。协议会以带 Protocol 名称后缀的接口形式导入,例如 @protocol Foo -> interface FooProtocol。这些类和接口会被放入构建配置中指定的包中(对于预配置的系统框架则是 platform.* 包)。

Kotlin 类和接口的名称在导入 Objective-C 时会加上前缀。该前缀来自 framework 名称。

Objective-C 在 framework 中不支持包。如果 Kotlin 编译器在同一个 framework 中发现名称相同但包不同的 Kotlin 类,会对它们重命名。该算法目前还不稳定,可能在不同 Kotlin 版本之间发生变化。为绕过这个问题,你可以重命名 framework 中冲突的 Kotlin 类。

强链接

只要你在 Kotlin 源码中使用某个 Objective-C 类,它就会被标记为强链接符号。生成的构建产物会把这些相关符号列为强外部引用。

这意味着应用在启动时会尝试动态链接这些符号,如果它们不可用,应用就会崩溃。即使这些符号从未被使用,崩溃也会发生。符号可能在特定设备或 OS 版本上不可用。

要绕过这个问题并避免 “Symbol not found” 错误,请使用检查该类是否真正可用的 Swift 或 Objective-C 包装器。查看 Compose Multiplatform framework 中是如何实现这一变通方案的。

初始化器

Swift/Objective-C 的初始化器会作为构造器或名为 create 的工厂方法导入 Kotlin。后者适用于在 Objective-C category 或 Swift 扩展中声明的初始化器,因为 Kotlin 没有扩展构造器的概念。

提示: 在把 Swift 初始化器导入 Kotlin 之前,别忘了用 @objc 注解标记它们。

Kotlin 构造器会作为初始化器导入 Swift/Objective-C。

Setter

覆盖父类只读属性的可写 Objective-C 属性,会表示为属性 foo 的 setFoo() 方法。对于以可变方式实现的协议只读属性,情况也是如此。

顶层函数和属性

顶层 Kotlin 函数和属性可以作为特殊类的成员访问。每个 Kotlin 文件都会转换为这样一个类,例如:

1
2
3
4
// MyLibraryUtils.kt
package my.library

fun foo() {}

然后你可以像这样从 Swift 调用 foo() 函数:

1
MyLibraryUtilsKt.foo()

在 Kotlin-Swift interopedia 中查看一组关于访问顶层 Kotlin 声明的示例:

方法名转换

一般来说,Swift 参数标签和 Objective-C 选择器片段会映射为 Kotlin 参数名。这两个概念语义不同,因此有时 Swift/Objective-C 方法导入后会与 Kotlin 签名发生冲突。在这种情况下,可以从 Kotlin 用命名实参调用这些冲突的方法,例如:

1
2
[player moveTo:LEFT byMeters:17]
[player moveTo:UP byInches:42]

在 Kotlin 中是这样的:

1
2
player.moveTo(LEFT, byMeters = 17)
player.moveTo(UP, byInches = 42)

kotlin.Any 的函数映射到 Swift/Objective-C 的方式如下:

| Kotlin | Swift | Objective-C |

| equals() | isEquals(_:) | isEquals: | | hashCode() | hash | hash | | toString() | description | description |

在 Kotlin-Swift interopedia 中查看数据类的示例。

你可以为 Swift 或 Objective-C 指定更地道的名称,而不必用 @ObjCName 注解重命名 Kotlin 声明。

错误与异常

所有 Kotlin 异常都是非受检的,即错误在运行时被捕获。而 Swift 只有编译期处理的受检错误。因此,如果 Swift 或 Objective-C 代码调用了会抛出异常的 Kotlin 方法,该 Kotlin 方法应使用 @Throws 注解标记,并指定一组“预期的”异常类。

编译为 Swift/Objective-C framework 时,带有或继承 @Throws 注解的非 suspend 函数在 Objective-C 中表示为产生 NSError* 的方法,在 Swift 中表示为 throws 方法。suspend 函数的表示在完成处理器中总是带有一个 NSError*/Error 参数。

当从 Swift/Objective-C 代码调用的 Kotlin 函数抛出某个由 @Throws 指定的类或其子类的异常实例时,该异常会作为 NSError 传播。其他到达 Swift/Objective-C 的 Kotlin 异常会被视为未处理,并导致程序终止。

不带 @Throws 的 suspend 函数只会传播 CancellationException(以 NSError 形式)。不带 @Throws 的非 suspend 函数完全不传播 Kotlin 异常。

请注意,反向的转换尚未实现:Swift/Objective-C 中会抛错的方法不会作为会抛异常的方法导入 Kotlin。

在 Kotlin-Swift interopedia 中查看示例。

枚举

Kotlin 枚举在 Objective-C 中作为 @interface 导入,在 Swift 中作为 class 导入。这些数据结构的属性与每个枚举值相对应。考虑以下 Kotlin 代码:

1
2
3
4
// Kotlin
enum class Colors {
    RED, GREEN, BLUE
}

你可以像这样从 Swift 访问这个枚举类的属性:

1
2
3
4
// Swift
Colors.red
Colors.green
Colors.blue

要在 Swift 的 switch 语句中使用 Kotlin 枚举变量,请提供 default 分支以避免编译错误:

1
2
3
4
5
6
switch color {
    case .red: print("It's red")
    case .green: print("It's green")
    case .blue: print("It's blue")
    default: fatalError("No such color")
}

在 Kotlin-Swift interopedia 中查看另一个示例。

挂起函数

实验性

Kotlin 的挂起函数(suspend)在生成的 Objective-C 头文件中表现为带回调的函数,用 Swift/Objective-C 的术语说就是完成处理器。

从 Swift 5.5 开始,Kotlin 的 suspend 函数也可以不使用完成处理器、而作为 async 函数从 Swift 调用。目前该功能仍高度实验性,并且有一些限制。细节请参阅这个 YouTrack issue。

扩展与 category 成员

Objective-C category 和 Swift 扩展的成员通常作为扩展导入 Kotlin。正因如此,这些声明在 Kotlin 中不能被覆盖,扩展初始化器也不能作为 Kotlin 构造器使用。

注意: 目前有两个例外。从 Kotlin 1.8.20 开始,与 NSView 类(来自 AppKit framework)或 UIView 类(来自 UIKit framework)声明在相同头文件中的 category 成员,会作为这些类的成员导入。这意味着你可以覆盖继承自 NSView 或 UIView 的方法。

对“普通”Kotlin 类的 Kotlin 扩展会分别作为扩展和 category 成员导入 Swift 和 Objective-C。对其他类型的 Kotlin 扩展会被视为带额外接收者参数的顶层声明。这些类型包括:

  • Kotlin String 类型
  • Kotlin 集合类型及其子类型
  • Kotlin interface 类型
  • Kotlin 原始类型
  • Kotlin inline 类
  • Kotlin Any 类型
  • Kotlin 函数类型及其子类型
  • Objective-C 类和协议

在 Kotlin-Swift interopedia 中查看一组示例。

Kotlin 单例

Kotlin 单例(用 object 声明创建,包括 companion object)会作为只有一个实例的类导入 Swift/Objective-C。

该实例可以通过 shared 和 companion 属性获取。

对于以下 Kotlin 代码:

1
2
3
4
5
6
7
8
9
object MyObject {
    val x = "Some value"
}

class MyClass {
    companion object {
        val x = "Some value"
    }
}

请按如下方式访问这些对象:

1
2
3
4
MyObject.shared
MyObject.shared.x
MyClass.companion
MyClass.Companion.shared

注意: 在 Objective-C 中通过 [MySingleton mySingleton] 访问对象、在 Swift 中通过 MySingleton() 访问对象的做法已被弃用。

在 Kotlin-Swift interopedia 中查看更多示例:

原始类型

Kotlin 原始类型的装箱值会映射到特殊的 Swift/Objective-C 类。例如,kotlin.Int 装箱值在 Swift 中表示为 KotlinInt 类实例(在 Objective-C 中表示为 ${prefix}Int 实例,其中 prefix 是 framework 的名称前缀)。这些类派生自 NSNumber,因此这些实例是真正的 NSNumber,支持所有相应操作。

当作为 Swift/Objective-C 参数类型或返回值使用时,NSNumber 类型不会被自动转换为 Kotlin 原始类型。原因是 NSNumber 类型没有提供关于所包装原始值类型的足够信息,例如从静态类型上并不知道 NSNumber 是 Byte、Boolean 还是 Double。因此 Kotlin 原始值应当手动与 NSNumber 相互转换。

字符串

当把 Kotlin String 传给 Swift 时,它会先导出为 Objective-C 对象,然后 Swift 编译器为了 Swift 转换再复制一次。这会带来额外的运行时开销。

为避免这一点,请在 Swift 中把 Kotlin 字符串直接作为 Objective-C 的 NSString 访问。查看转换示例。

NSMutableString

Objective-C 的 NSMutableString 类在 Kotlin 中不可用。所有 NSMutableString 实例在传给 Kotlin 时都会被复制。

集合

Kotlin -> Objective-C -> Swift

当 Kotlin 集合传给 Swift 时,它会先转换为 Objective-C 的对应类型,然后 Swift 编译器会复制整个集合并把它转换为 Swift 原生集合,如映射表中所述。

最后这次转换会带来性能开销。为避免这种开销,在 Swift 中使用 Kotlin 集合时,请把它们显式转换为对应的 Objective-C 类型:NSDictionary、NSArray 或 NSSet。

查看转换示例

例如,以下 Kotlin 声明:

1
val map: Map<String, String>

在 Swift 中它看起来像这样:

1
map[key]?.count ?? 0

这里 map 被隐式转换为 Swift 的 Dictionary,其字符串值被映射为 Swift 的 String。这会带来性能开销。

为避免这种转换,请把 map 显式转换为 Objective-C 的 NSDictionary,并改为以 NSString 访问值:

1
2
let nsMap: NSDictionary = map as NSDictionary
(nsMap[key] as? NSString)?.length ?? 0

这样就能确保 Swift 编译器不执行额外的转换步骤。

Swift -> Objective-C -> Kotlin

Swift/Objective-C 集合会如映射表中所述映射到 Kotlin,但 NSMutableSet 和 NSMutableDictionary 除外。

NSMutableSet 不会被转换为 Kotlin 的 MutableSet。要把对象传给 Kotlin 的 MutableSet,请显式创建这种 Kotlin 集合。为此,你可以使用 Kotlin 中的 mutableSetOf() 函数,或 Swift 中的 KotlinMutableSet 类以及 Objective-C 中的 ${prefix}MutableSet(prefix 是 framework 的名称前缀)。MutableMap 也是如此。

在 Kotlin-Swift interopedia 中查看示例。

函数类型

Kotlin 函数类型的对象(例如 lambda)在 Swift 中转换为闭包,在 Objective-C 中转换为 block。在 Kotlin-Swift interopedia 中查看带 lambda 的 Kotlin 函数示例。

不过,在转换函数和函数类型时,参数类型和返回值的映射方式有所不同。在后一种情况下,原始类型会映射为其装箱表示。Kotlin 的 Unit 返回值在 Swift/Objective-C 中表示为相应的 Unit 单例。该单例的值可以像访问任何其他 Kotlin object 那样获取。请参阅上表中的单例部分。

考虑以下 Kotlin 函数:

1
fun foo(block: (Int) -> Unit) { ... }

它在 Swift 中表示如下:

1
func foo(block: (KotlinInt) -> KotlinUnit)

你可以像这样调用它:

1
2
3
4
foo {
    bar($0 as! Int32)
    return KotlinUnit()
}

Objective-C block 类型中的显式参数名

实验性

你可以为导出的 Objective-C 头文件给 Kotlin 的函数类型添加显式参数名。这样,Xcode 的自动补全在 Objective-C block 中调用 Objective-C 函数时就会建议这些名称。这有助于避免生成的 block 中出现 Clang 警告。

要启用显式参数名,请把以下二进制选项添加到你的 gradle.properties 文件中:

kotlin.native.binary.objcExportBlockExplicitParameterNames=true

例如,对于以下 Kotlin 代码:

1
2
// Kotlin:
fun greetUser(block: (name: String) -> Unit) = block("John")

Kotlin 会把 Kotlin 函数类型中的参数名转发到 Objective-C block 类型,从而让 Xcode 可以在建议中使用它们:

1
2
3
4
// Objective-C:
greetUserBlock:^(NSString *name) {
    // ...
};

注意: 该选项只影响 Objective-C 互操作。它适用于在 Xcode 中从 Objective-C 调用生成的 Objective-C 代码,通常不影响从 Swift 的调用。

泛型

Objective-C 支持类中定义的“轻量级泛型”,但功能相对有限。Swift 可以导入类上定义的泛型,以帮助向编译器提供额外的类型信息。

Objective-C 和 Swift 对泛型的支持与 Kotlin 不同,因此转换不可避免地会丢失一些信息,但受支持的特性仍会保留有意义的信息。

关于如何在 Swift 中使用 Kotlin 泛型的具体示例,请参阅 Kotlin-Swift interopedia。

限制

Objective-C 泛型并不支持 Kotlin 或 Swift 的所有特性,因此转换中会丢失一些信息。

泛型只能定义在类上,不能定义在接口(Objective-C 和 Swift 中的协议)或函数上。

可空性

Kotlin 和 Swift 都把可空性定义为类型规格的一部分,而 Objective-C 把可空性定义在类型的方法和属性上。因此,以下 Kotlin 代码:

1
2
3
class Sample<T>() {
    fun myVal(): T
}

在 Swift 中看起来像这样:

1
2
3
class Sample<T>() {
    fun myVal(): T?
}

为了支持可能为空的类型,Objective-C 头文件需要把 myVal 定义为可空返回值。

为缓解这一点,在定义泛型类时,如果该泛型类型_绝不_应为空,请提供不可为空的类型约束:

1
2
3
class Sample<T : Any>() {
    fun myVal(): T
}

这会强制 Objective-C 头文件把 myVal 标记为不可为空。

型变

Objective-C 允许把泛型声明为协变或逆变。Swift 不支持型变。来自 Objective-C 的泛型类可以按需强制转换。

1
2
data class SomeData(val num: Int = 42) : BaseData()
class GenVarOut<out T : Any>(val arg: T)
1
2
let variOut = GenVarOut<SomeData>(arg: sd)
let variOutAny : GenVarOut<BaseData> = variOut as! GenVarOut<BaseData>

约束

在 Kotlin 中,你可以为泛型类型提供上界。Objective-C 也支持这一点,但在更复杂的情况下这种支持不可用,目前在 Kotlin 与 Objective-C 的互操作中也不受支持。唯一的例外是不可为空的上界会让 Objective-C 方法/属性变为不可为空。

禁用

要让 framework 头文件在生成时不包含泛型,请在构建文件中添加以下编译器选项:

1
2
3
binaries.framework {
    freeCompilerArgs += "-Xno-objc-generics"
}

前向声明

要导入前向声明,请使用 objcnames.classes 和 objcnames.protocols 包。例如,要导入在包为 library.package 的 Objective-C 库中声明的 objcprotocolName 前向声明,请使用特殊的前向声明包:import objcnames.protocols.objcprotocolName。

考虑两个 objcinterop 库:一个使用 objcnames.protocols.ForwardDeclaredProtocolProtocol,另一个在另一个包中包含实际实现:

1
2
3
4
5
6
7
8
// 第一个 objcinterop 库
#import <Foundation/Foundation.h>

@protocol ForwardDeclaredProtocol;

NSString* consumeProtocol(id<ForwardDeclaredProtocol> s) {
    return [NSString stringWithUTF8String:"Protocol"];
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 第二个 objcinterop 库
// 头文件:
#import <Foundation/Foundation.h>
@protocol ForwardDeclaredProtocol
@end
// 实现:
@interface ForwardDeclaredProtocolImpl : NSObject <ForwardDeclaredProtocol>
@end

id<ForwardDeclaredProtocol> produceProtocol() {
    return [ForwardDeclaredProtocolImpl new];
}

要在两个库之间传递对象,请在 Kotlin 代码中使用显式的 as 转换:

1
2
3
4
// Kotlin 代码:
fun test() {
    consumeProtocol(produceProtocol() as objcnames.protocols.ForwardDeclaredProtocolProtocol)
}

注意: 你只能从对应的真实类转换到 objcnames.protocols.ForwardDeclaredProtocolProtocol。否则会报错。

映射类型之间的转换

在编写 Kotlin 代码时,可能需要把对象从 Kotlin 类型转换为等价的 Swift/Objective-C 类型,或者反向转换。在这种情况下,你可以使用 as 转换,例如:

1
2
3
4
5
6
7
@file:Suppress("CAST_NEVER_SUCCEEDS")
import platform.Foundation.*

val nsNumber = 42 as NSNumber
val nsArray = listOf(1, 2, 3) as NSArray
val nsString = "Hello" as NSString
val string = nsString as String

IDE 可能会错误地发出 “This cast can never succeed” 警告。在这种情况下,请使用 @Suppress("CAST_NEVER_SUCCEEDS") 注解。

子类化

在 Swift/Objective-C 中子类化 Kotlin 类和接口

Kotlin 类和接口可以由 Swift/Objective-C 的类和协议子类化。

在 Kotlin 中子类化 Swift/Objective-C 类和协议

Swift/Objective-C 的类和协议可以用 Kotlin 的 final 类子类化。继承 Swift/Objective-C 类型的非 final Kotlin 类尚不受支持,因此无法声明继承 Swift/Objective-C 类型的复杂类层次结构。

普通方法可以用 Kotlin 的 override 关键字覆盖。在这种情况下,覆盖方法必须与被覆盖方法具有相同的参数名。

有时需要覆盖初始化器,例如在子类化 UIViewController 时。以 Kotlin 构造器形式导入的初始化器,可以由带 @OverrideInit 注解的 Kotlin 构造器覆盖:

1
2
3
4
5
class ViewController : UIViewController {
    @OverrideInit constructor(coder: NSCoder) : super(coder)

    ...
}

覆盖构造器必须与被覆盖构造器具有相同的参数名和类型。

要覆盖 Kotlin 签名冲突的不同方法,你可以给类添加 @ObjCSignatureOverride 注解。当从 Objective-C 类继承了若干实参类型相同但实参名不同的函数时,该注解指示 Kotlin 编译器忽略这些冲突的重载。

默认情况下,Kotlin/Native 编译器不允许把非指定的 Objective-C 初始化器作为 super() 构造器调用。如果 Objective-C 库中没有正确标记指定初始化器,这种行为可能会带来不便。要禁用这些编译器检查,请把 disableDesignatedInitializerChecks = true 添加到该库的 .def 文件中。

C 特性

关于库使用普通 C 特性(例如不安全指针、结构体等)的示例,请参阅与 C 互操作。

不支持的特性

Kotlin 编程语言的某些特性尚未映射到 Objective-C 或 Swift 的相应特性。目前,以下特性在生成的 framework 头文件中没有得到适当的暴露:

  • 内联类(实参被映射为底层原始类型或 id)
  • 实现标准 Kotlin 集合接口(List、Map、Set)的自定义类及其他特殊类
  • Objective-C 类的 Kotlin 子类