Kotlin Native iOS 字符串格式可变参数
Kotlin Native iOS string formatting with vararg
基于 this issue 关于使用 NSString
格式化,我尝试在使用 vararg
时实现格式化的多平台实现,到目前为止没有成功。
我做了什么
- 已添加FoundationInterop.def
language = Objective-C
---
#import <Foundation/NSString.h>
NSString* format(NSString* format, ...) {
va_list args;
va_start(args, format);
NSString* result = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
return result;
}
- 在gradle
中编译
targets {
final def iOSTarget = System.getenv('SDK_NAME')?.startsWith("iphoneos") \
? presets.iosArm64 : presets.iosX64
// https://kotlinlang.org/docs/reference/mpp-dsl-reference.html#native-targets
fromPreset(iOSTarget, 'ios') {
binaries {
}
compilations.main.cinterops {
FoundationInterop {
}
}
}
}
- 在
commonMain
中创建 StringExtensions.kt
expect class StringType
expect fun String.format(format: String, vararg args: Any?): StringType?
- 在
iosMain
actual typealias StringType = String
/**
* https://github.com/JetBrains/kotlin-native/issues/1834
*/
actual fun String.format(format: String, vararg args: Any?): StringType? {
return FoundationInterop.format(format, args as Any)
}
- 例子
val fmt = "http://geomag.bgs.ac.uk/web_service/GMModels/igrf/13/?latitude=%f&longitude=%f&altitude=0&date=%d-%02d-%02d&format=json"
val url = fmt.format(urlFmt, 59.127934932762166, 38.00503518930868, 2020, 1, 3)
- 输出 - 如您所见,由于某种原因没有发生值替换
http://geomag.bgs.ac.uk/web_service/GMModels/igrf/13/?latitude=0.000000&longitude=0.000000&altitude=0&date=43344272-198763328-00&format=json
编辑
stringWithFormat
给出相同的结果
actual fun String.format(format: String, vararg args: Any?): StringType? {
return NSString.stringWithFormat(format, args as Any)
}
编辑 2
我确认你所说的NSString.stringWithFormat
。正如我们在 JB 的官方回答中所读到的那样,该功能缺失
Svyatoslav Scherbina,我们可以在这里关注您的问题:KT-42925
作为一个糟糕的解决方法,我提出了类似的建议(警告:不详尽,没有很多索引计数检查...)
import platform.Foundation.NSString
import platform.Foundation.stringWithFormat
actual typealias StringType = String
actual fun String.format(format: String, vararg args: Any?): StringType? {
var returnString = ""
val regEx = "%[\d|.]*[sdf]|[%]".toRegex()
val singleFormats = regEx.findAll(format).map {
it.groupValues.first()
}.asSequence().toList()
val newStrings = format.split(regEx)
for (i in 0 until args.count()) {
val arg = args[i]
returnString += when (arg) {
is Double -> {
NSString.stringWithFormat(newStrings[i] + singleFormats[i], args[i] as Double)
}
is Int -> {
NSString.stringWithFormat(newStrings[i] + singleFormats[i], args[i] as Int)
}
else -> {
NSString.stringWithFormat(newStrings[i] + "%@", args[i])
}
}
}
return returnString
}
但看看它是否适合您。
您不能将 Kotlin 可变参数转发给 C 或 Objective-C。 C 变量是编译时间。
这不是 Kotlin-specific 限制。您不能通过转发所有参数从另一个可变参数 C 函数调用可变参数 C 函数。
NSString.stringWithFormat(format, args as Any)
这是不正确的。
此行采用 args
(这是一个 Array
),将其转换为 Any
并作为单个参数传递。
所以这基本上等同于
[NSString stringWithFormat:format, createKotlinArray(/* all arguments here */)]
这不符合您的预期。
因此 KT-42925 无效。
您的问题可以通过缺少的功能之一解决:
- stdlib (KT-25506) 中的常见
String.format
。但这并不容易。
- 支持动态构建
va_list
,例如来自 Kotlin Array
(KT-42973). In this case it would be easy to use this variant: https://developer.apple.com/documentation/foundation/nsstring/1407827-initwithformat
基于 this issue 关于使用 NSString
格式化,我尝试在使用 vararg
时实现格式化的多平台实现,到目前为止没有成功。
我做了什么
- 已添加FoundationInterop.def
language = Objective-C
---
#import <Foundation/NSString.h>
NSString* format(NSString* format, ...) {
va_list args;
va_start(args, format);
NSString* result = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
return result;
}
- 在gradle 中编译
targets {
final def iOSTarget = System.getenv('SDK_NAME')?.startsWith("iphoneos") \
? presets.iosArm64 : presets.iosX64
// https://kotlinlang.org/docs/reference/mpp-dsl-reference.html#native-targets
fromPreset(iOSTarget, 'ios') {
binaries {
}
compilations.main.cinterops {
FoundationInterop {
}
}
}
}
- 在
commonMain
中创建
StringExtensions.kt
expect class StringType
expect fun String.format(format: String, vararg args: Any?): StringType?
- 在
iosMain
actual typealias StringType = String
/**
* https://github.com/JetBrains/kotlin-native/issues/1834
*/
actual fun String.format(format: String, vararg args: Any?): StringType? {
return FoundationInterop.format(format, args as Any)
}
- 例子
val fmt = "http://geomag.bgs.ac.uk/web_service/GMModels/igrf/13/?latitude=%f&longitude=%f&altitude=0&date=%d-%02d-%02d&format=json"
val url = fmt.format(urlFmt, 59.127934932762166, 38.00503518930868, 2020, 1, 3)
- 输出 - 如您所见,由于某种原因没有发生值替换
http://geomag.bgs.ac.uk/web_service/GMModels/igrf/13/?latitude=0.000000&longitude=0.000000&altitude=0&date=43344272-198763328-00&format=json
编辑
stringWithFormat
给出相同的结果
actual fun String.format(format: String, vararg args: Any?): StringType? {
return NSString.stringWithFormat(format, args as Any)
}
编辑 2
我确认你所说的NSString.stringWithFormat
。正如我们在 JB 的官方回答中所读到的那样,该功能缺失
Svyatoslav Scherbina,我们可以在这里关注您的问题:KT-42925
作为一个糟糕的解决方法,我提出了类似的建议(警告:不详尽,没有很多索引计数检查...)
import platform.Foundation.NSString
import platform.Foundation.stringWithFormat
actual typealias StringType = String
actual fun String.format(format: String, vararg args: Any?): StringType? {
var returnString = ""
val regEx = "%[\d|.]*[sdf]|[%]".toRegex()
val singleFormats = regEx.findAll(format).map {
it.groupValues.first()
}.asSequence().toList()
val newStrings = format.split(regEx)
for (i in 0 until args.count()) {
val arg = args[i]
returnString += when (arg) {
is Double -> {
NSString.stringWithFormat(newStrings[i] + singleFormats[i], args[i] as Double)
}
is Int -> {
NSString.stringWithFormat(newStrings[i] + singleFormats[i], args[i] as Int)
}
else -> {
NSString.stringWithFormat(newStrings[i] + "%@", args[i])
}
}
}
return returnString
}
但看看它是否适合您。
您不能将 Kotlin 可变参数转发给 C 或 Objective-C。 C 变量是编译时间。 这不是 Kotlin-specific 限制。您不能通过转发所有参数从另一个可变参数 C 函数调用可变参数 C 函数。
NSString.stringWithFormat(format, args as Any)
这是不正确的。
此行采用 args
(这是一个 Array
),将其转换为 Any
并作为单个参数传递。
所以这基本上等同于
[NSString stringWithFormat:format, createKotlinArray(/* all arguments here */)]
这不符合您的预期。
因此 KT-42925 无效。 您的问题可以通过缺少的功能之一解决:
- stdlib (KT-25506) 中的常见
String.format
。但这并不容易。 - 支持动态构建
va_list
,例如来自 KotlinArray
(KT-42973). In this case it would be easy to use this variant: https://developer.apple.com/documentation/foundation/nsstring/1407827-initwithformat