gpt4 book ai didi

objective-c - 如何将Objective-C语言(“va_list”,“va_start”,“va_end”)转换为快速语言?

转载 作者:行者123 更新时间:2023-11-30 11:02:08 24 4
gpt4 key购买 nike

这是我的代码。它们是Objective-C语言:

- (void)objcMethod:(NSString *)format, ...
{
va_list args;

va_start(args, format);

NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
NSLog(@"%@", msg);

va_end(args);

va_start(args, format);

va_end(args);
}


如何将Objective-C语言( va_listva_startva_end)转换为快速语言?

我还需要在Objective-C xxx.m文件中调用此swift方法。

需要帮忙。谢谢!

================================================== ====================

更新:

我尝试了MartinR的答案 NSLog is unavailable,但是出了点问题,我无法在方法前添加@objc,需要help.thanks。

my codes...

最佳答案

通过@MartinR注释和对NSLog is unavailable的引用,您知道Swift可以通过使用Swift类型va_listCVarArg来调用采用CVaListPointer参数的(Objective-)C函数和方法。

许多常见的(Objective-C)可变参数函数和方法都有一个采用va_list的同级,因此Swift中的这种支持提供了对它们的访问。


  我还需要在Objective-C xxx.m文件中调用此swift方法。


但是,您希望采用另一种方法,并且编写了Objective-C方法的Swift可变参数函数版本后,您发现无法调用它。您试图问什么解决方案How do you call a Swift variadic method from Objective-C?,该问题的间接答案(您的问题被标记为重复)提供了一个提示-使用数组-但无法处理格式化打印所需的一般性键入方案。让我们看看是否可以到达那里...

(使用Xcode 10 / Swift 4.2,其他任何版本的Swift可能都不同。)

我们将使用以下Swift类作为基础:

class SwiftLog : NSObject
{
// Swift entry point
static func Log(_ format : String, args : CVarArg...)
{
withVaList(args) { LogV(format, $0)}
}

// Shared core
private static func LogV(_ format : String, _ args: CVaListPointer)
{
NSLogv(format, args)
}
}


这为Swift提供了可变参数函数,该函数将采用您可能感兴趣的所有Swift库类型,而您不需要的其他类型( Apple's CVarArg documentation中列出了3287)。这里的私有核心功能微不足道,您可能希望做一些更多的事情。

现在,您希望从Objective-C调用 Log(),但是,正如您所发现的,由于 CVarArg而不能。但是,Objective-C可以调用带有 NSObject参数的Swift函数,而 NSObject实现 CVarArg,这使我们第一次尝试:

   // Objective-C entry point
@objc static func Log(_ format : String, args : [NSObject])
{
withVaList(args) { LogV(format, $0) }
}


这按原样工作,但是每个参数都必须是一个对象,并使用 %@设置格式,然后切换到Objective-C:

[SwiftLog LogObjects:@"%@|%@|%@|%@|%@|%@" args:@[@"42", @4.2, @"hello", @31, @'c', NSDate.new]];


产生:

42|4.2|hello|31|99|Sun Nov 11 08:47:35 2018


它在一定范围内有效,我们失去了格式设置的灵活性–没有 %6.2f%x等–并且字符以 99出现。

我们可以改善吗?如果您准备牺牲按原样打印 NSNumber值的功能,则可以。在Swift中,将 Log()函数更改为:

   @objc static func Log(_ format : String, args : [NSObject])
{
withVaList(args.map(toPrintfArg)) { LogV(format, $0) }
}


在Objective-C中暂时跳过 toPrintfArg(它又大又丑),我们可以将此版本称为:

[SwiftLog Log:@"%@|%4.2f|%10s|%x|%c|%@" args:@[@"42", @4.2, @((intptr_t)"hello"), @31, @'c', NSDate.new]];


产生:

42|4.20|     hello|1f|c|Sun Nov 11 08:47:35 2018


更好,而且字符正确。那么 toPrintfArg是做什么的呢?

在上面的代码中,我们必须将对象数组传递给Swift,然后将所有原始值包装为 NSNumber对象。

在Objective-C中, NSNumber对象并没有透露太多有关包装的内容,访问方法( .doubleValue.integerValue等)会将包装的值转换为所请求类型的值并返回它。

但是, NSNumber是免费的桥接到Core Foundation类型的 CFBooleanCFNumber。前者用于布尔值(很明显!),后者用于所有其他数字类型,并且与 NSNumber不同,它提供了一个返回包装值类型的函数,因此可以在不进行转换的情况下将其展开。使用这些信息,我们可以从 NSNumber对象中提取原始值(专家,是的,请参见下文),所有在Swift中提取的值都将实现 CVarArg,如下所示:

   private static func toPrintfArg(_ item : NSObject) -> CVarArg
{
if let anumber = item as? NSNumber
{
if type(of:anumber) == CFBoolean.self { return anumber.boolValue }

switch CFNumberGetType(anumber)
{
case CFNumberType.sInt8Type: return anumber.int8Value
case CFNumberType.sInt16Type: return anumber.int16Value
case CFNumberType.sInt32Type: return anumber.int32Value
case CFNumberType.sInt64Type: return anumber.int64Value
case CFNumberType.float32Type: return Float32(anumber.floatValue)
case CFNumberType.float64Type: return Float64(anumber.doubleValue)
case CFNumberType.charType: return CChar(anumber.int8Value)
case CFNumberType.shortType: return CShort(anumber.int16Value)
case CFNumberType.intType: return CInt(anumber.int32Value)
case CFNumberType.longType: return CLong(anumber.int64Value)
case CFNumberType.longLongType: return CLongLong(anumber.int64Value)
case CFNumberType.floatType: return anumber.floatValue
case CFNumberType.doubleType: return anumber.doubleValue
case CFNumberType.cfIndexType: return CFIndex(anumber.int64Value)
case CFNumberType.nsIntegerType: return NSInteger(anumber.int64Value)
case CFNumberType.cgFloatType: return CGFloat(anumber.doubleValue)
}
}

return item;
}


此函数会将 NSNumber对象解包(专家,是的,大多数情况,请参见下文),将其保留为原始值类型,同时将所有其他对象保留为 %@格式(如 NSStringNSDate对象所示)在示例中)。

希望能有所帮助,至少不至于造成混淆!好奇/专家注意事项如下。



注意事项和警告

保留C指针

在上面的示例中,通过将C字符串 "hello"转换为 intptr_t来传递,C整数类型的大小与指针大小相同,而不是指针值。在这种情况下,这很好, va_list本质上是无类型的字节鲍勃,并且格式告诉 NSLogv()将下一个字节解释为哪种类型,转换为 intptr_t会保留相同的字节/位并允许指针指向被包装为 NSNumber

但是,如果您的应用程序需要在Swift端具有实际的指针,则可以将C字符串包装为 NSValue

[NSValue valueWithPointer:"hello"]


并通过添加以下内容将其解包到 toPrintfArg中:

      if let ptr = (item as? NSValue)?.pointerValue
{
return ptr.bindMemory(to: Int8.self, capacity: 1)
}


这将产生一个类型为 UnsafeMutablePointer<Int8>的值,该值实现了 CVarArg(对于后者, capacity是无关紧要的)。

您是否总是返回相同的类型?

如果将C类型包装为 NSNumber,然后如上所述将其拆开,则类型可能会由于参数提升而改变(这意味着小于 int的整数类型将作为 int值传递, float值将作为 double)在C中?答案可能是,但是 CVarArg值的必需类型是提升的类型,因此在这种情况下它应该没有任何区别–与预期格式说明符相匹配的未包装值的类型。

NSDecimalNumber呢?

很好发现,如果尝试打印 NSDecimalNumber(它是 NSNumber的子类),则上述 toPrintfArg会将其解压缩为 double,并且必须使用浮点格式而不是 %@。处理此问题留作练习。

关于objective-c - 如何将Objective-C语言(“va_list”,“va_start”,“va_end”)转换为快速语言?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53220674/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com