Kotlin Native iOS 字符串格式化与 vararg

问题描述 投票:0回答:3

基于关于使用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 {
                }
            }
        }
    }
  • 创建
    StringExtensions.kt
    commonMain
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

已创建问题 https://youtrack.jetbrains.com/issue/KT-42925

objective-c kotlin gradle kotlin-multiplatform kotlin-native
3个回答
3
投票

我确认你所说的

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
}

但是看看这是否对您来说是一个有效的解决方法。


2
投票

您无法将 Kotlin 可变参数转发给 C 或 Objective-C。 C 变量是编译时的。 这不是 Kotlin 特有的限制。您无法从另一个可变参数 C 函数调用可变参数 C 函数并转发所有参数。

NSString.stringWithFormat(format, args as Any)

这是不正确的。 此行接受

args
(这是一个
Array
),将其转换为
Any
并作为单个参数传递。

所以这基本上相当于

[NSString stringWithFormat:format, createKotlinArray(/* all arguments here */)]

这并没有达到你的预期。

因此 KT-42925 无效。 您的问题可能可以通过缺少的功能之一来解决:


0
投票

如果您仅通过将

args
传递给方法来转发它,它将被视为
vararg
的单个元素,要将其作为整体传递,您需要使用命名参数来调用它,如下所示:

NSString.stringWithFormat(format, args = arguments as Array<Any?>)
© www.soinside.com 2019 - 2024. All rights reserved.