[英]how to transfer objective-c language (`va_list`,`va_start `,`va_end ` ) to swift language?
這是我的代碼。 它們是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_list
, va_start
, va_end
)轉換為快速語言?
我還需要在Objective-C xxx.m文件中調用此swift方法。
需要幫忙。 謝謝!
================================================== ====================
更新:
我嘗試了MartinR的答案NSLog不可用 ,但是出了點問題,我無法在方法前添加@objc,需要help.thanks。
通過@MartinR注釋和對NSLog的引用不可用,您知道Swift可以通過使用Swift類型CVarArg
和CVaListPointer
使用va_list
參數的(Objective-)C函數和方法。
許多常見的(Objective-)C可變參數函數和方法都有一個va_list
的兄弟對象,因此Swift中的這種支持提供了對它們的訪問。
我還需要在Objective-C xxx.m文件中調用此swift方法。
但是,您希望采用另一種方法,並且編寫了Objective-C方法的Swift可變參數函數版本后,您發現無法調用它。 您試圖問解決方案是什么, 如何從Objective-C調用Swift可變參數方法? ,該問題的間接答案(您的問題被標記為重復)為您提供了使用數組的提示,但不能滿足格式化打印類型方案所需的一般性。 讓我們看看是否可以到達那里...
(使用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的CVarArg
文檔中列出了3287)。 這里的私有核心功能微不足道,您可能希望做一些更多的事情。
現在,您希望從Objective-C調用Log()
,但是,正如您所發現的,由於CVarArg
,您不能。 但是,Objective-C可以調用CVarArg
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
( 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類型CFBoolean
和CFNumber
是“免費橋接”的; 前者用於布爾值(很明顯!),而后者用於所有其他數字類型,並且與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
對象解包(專家,是的,大多數情況,請參見下文),恢復為原始值類型,同時保留所有其他對象按%@
格式設置的格式(如示例中的NSString
和NSDate
對象所示)。
希望能有所幫助,至少不至於造成混淆! 好奇/專家注意事項如下。
注意事項和警告
保留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
傳遞)? 答案可能是,但是CVarArg
值的必需類型是提升的類型,因此在這種情況下 ,它不應有任何區別–符合預期格式說明符的未包裝值的類型。
那NSDecimalNumber
呢?
那么發現,如果你嘗試打印一個NSDecimalNumber
,這是一個子類NSNumber
,上述toPrintfArg
將解開它作為一個double
,你必須使用浮點格式,而不是%@
。 處理此問題留作練習。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.