簡體   English   中英

如何將Objective-C語言(“ va_list”,“ va_start”,“ va_end”)轉換為快速語言?

[英]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_listva_startva_end )轉換為快速語言?

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

需要幫忙。 謝謝!

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

更新:

我嘗試了MartinR的答案NSLog不可用 ,但是出了點問題,我無法在方法前添加@objc,需要help.thanks。

我的密碼...

通過@MartinR注釋和對NSLog的引用不可用,您知道Swift可以通過使用Swift類型CVarArgCVaListPointer使用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中跳過toPrintfArgtoPrintfArg大又丑),我們可以將此版本稱為:

[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傳遞)? 答案可能但是CVarArg值的必需類型是提升的類型,因此在這種情況下 ,它不應有任何區別–符合預期格式說明符的未包裝值的類型。

NSDecimalNumber呢?

那么發現,如果你嘗試打印一個NSDecimalNumber ,這是一個子類NSNumber ,上述toPrintfArg將解開它作為一個double ,你必須使用浮點格式,而不是%@ 處理此問題留作練習。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM