如何以伪装__SwiftValue的形式获取swift结构的实际类型

问题描述

我正在使用YapDatabase编码/解码我的Swift值类型。解码后,类型信息似乎丢失了,即type(of:element)返回__SwiftValue而不是例如Reservation

但是如果我在调试器中调用po element,似乎类型信息仍然保留:

(lldb) po element 
SecureTruckParking.Reservation(reservationId: 12625,accessinformations: [SecureTruckParking.Accessinformation(accessinformationId: 12706,accesstypeId: 1,accesstypeKey: Optional("1"),accesstypeTenantKey: Optional("ROOT"),encodedValue: "XXX",displayedValue: "XXX"),SecureTruckParking.Accessinformation(accessinformationId: 12707,accesstypeId: 51,accesstypeKey: Optional("51"),encodedValue: "918296",displayedValue: "918296")],customerId: 3156,areaId: 552,productId: 1004,state: "PENDING",startTime: 2020-09-10 08:23:00 +0000,endTime: 2020-09-11 08:23:00 +0000,earliestEntryTime: 2020-09-10 08:23:00 +0000,latestExitTime: 2020-09-11 08:23:00 +0000,totalAmount: 2750.0,currency: "€",netPrice: 2311.0,taxPrice: 439.0,invoiceItems: [SecureTruckParking.InvoiceItem(amount: 1,itemText: "Parkplatzreservierung,10.09.2020 10:23 - 11.09.2020 10:23 \nREWE Logistikzentrum Neu-Isenburg -> REWE Logistikzentrum Neu-Isenburg",taxRate: 19.0)],productAttributes: [SecureTruckParking.Attribute(key: "early_bird_count",value: Optional("1"),deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "early_bird_count",tenant: "ROOT"))),SecureTruckParking.Attribute(key: "DETAILS",value: nil,deFinitionId: nil),SecureTruckParking.Attribute(key: "INFO_DETAILS",SecureTruckParking.Attribute(key: "early_bird",value: Optional("false"),deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "early_bird",SecureTruckParking.Attribute(key: "manualBackofficeCancellationConfirmation",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "manualBackofficeCancellationConfirmation",SecureTruckParking.Attribute(key: "manualBackofficeCancellationConfirmationThreshold",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "manualBackofficeCancellationConfirmationThreshold",SecureTruckParking.Attribute(key: "product_icon",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "product_icon",SecureTruckParking.Attribute(key: "product_image",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "product_image",SecureTruckParking.Attribute(key: "product_tariff_group",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "product_tariff_group",SecureTruckParking.Attribute(key: "upgrading",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "upgrading",SecureTruckParking.Attribute(key: "upgrading_dates_fixed",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "upgrading_dates_fixed",SecureTruckParking.Attribute(key: "upgrading_immediate",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "upgrading_immediate",SecureTruckParking.Attribute(key: "upgrading_mail",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "upgrading_mail",SecureTruckParking.Attribute(key: "upgrading_price_surcharge",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "upgrading_price_surcharge",SecureTruckParking.Attribute(key: "early_bird_duration",value: Optional("1440"),deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "early_bird_duration",SecureTruckParking.Attribute(key: "recurring_max_trips",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "recurring_max_trips",SecureTruckParking.Attribute(key: "upgrading_mail_max_lead_time",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "upgrading_mail_max_lead_time",SecureTruckParking.Attribute(key: "upgrading_mail_min_lead_time",deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "upgrading_mail_min_lead_time",SecureTruckParking.Attribute(key: "bstp_product_type",value: Optional("Reservation"),deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "bstp_product_type",tenant: "ROOT")))],areaAttributes: [SecureTruckParking.Attribute(key: "product_icon",SecureTruckParking.Attribute(key: "Parking_Area_UST",value: Optional("22222222222222"),deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "Parking_Area_UST",tenant: "MAN_BOSCH"))),SecureTruckParking.Attribute(key: "ipaw_id",value: Optional("4651"),deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "ipaw_id",SecureTruckParking.Attribute(key: "bstp_area_type",value: Optional("PROFESSIONAL"),deFinitionId: Optional(SecureTruckParking.DeFinitionId(key: "bstp_area_type",tenant: "ROOT")))])

这是什么__SwiftValue,有没有一种方法可以获取实际的类型(解析String(describing: element的任何可怕方法之外)?

解决方法

首先,您会看到__SwiftValue,因为struct已被as AnyObject强制转换包裹。您可以轻松地自己验证:

struct TestStruct {}
let ourStruct = TestStruct()
let structSwiftValue = ourStruct as AnyObject
print(type(of: ourStruct))
print(type(of: structSwiftValue))

输出:

TestStruct
__SwiftValue

关于以下主题的精彩讨论:https://forums.swift.org/t/anyobject/35659/9

现在,就您问题的本质而言,很不幸,我认为暂时没有便捷的公共API可以让您从Type中提取原始元数据__SwiftValue

这个话题仍然很有趣,不能冒险进入私有API领域。

让我们从__SwiftValue本身https://github.com/apple/swift/blob/master/stdlib/public/runtime/SwiftValue.mm

开始

通过obj-c提供了很有前途的API:

// Private methods for debugging purposes.

- (const Metadata *)_swiftTypeMetadata {
  return getSwiftValueTypeMetadata(self);
}
- (id /* NSString */)_swiftTypeName {
  TypeNamePair typeName
    = swift_getTypeName(getSwiftValueTypeMetadata(self),true);
  id str = swift_stdlib_NSStringFromUTF8(typeName.data,typeName.length);
  return [str autorelease];
}
- (const OpaqueValue *)_swiftValue {
  return getValueFromSwiftValue(self).second;
}

让我们从一个非常简单的事情开始:

print(structSwiftValue.value(forKey: "_swiftTypeName")!)

输出:

ModuleName.TestStruct

仅为String类型,但对于入门者来说还不错。下一个有希望探索的候选人似乎是:

- (const Metadata *)_swiftTypeMetadata()

我们可以这样称呼(有关在Swift中调用选择器的特定方式的详细信息 here

let selector: Selector = NSSelectorFromString("_swiftTypeMetadata")
let methodIMP: IMP! = structSwiftValue.method(for: selector)
let metadataPtr = unsafeBitCast(methodIMP,to:(@convention(c)(Any?,Selector)->OpaquePointer).self)(structSwiftValue,selector)

现在的挑战是如何以一种Metadata *的方式利用OpaquePointer(即桥接到Swift之后的type(of:))。

我找到了一种方法,尽管它很笨拙。
我们从一些虚拟对象或结构的元数据Type变量开始(并不重要)。总体思路是最终我们希望将元数据指针替换为上一阶段获得的指针。我注意到type(of:)的结果产生了一个指向元数据的指针,因此存在额外的间接级别,我们必须为此提供适应性:

var placeholderTypeVar: Any.Type = type(of: NSObject())
print(placeholderTypeVar)
withUnsafeMutablePointer(to: &placeholderTypeVar) {
    let unsafePtr = UnsafeMutablePointer<OpaquePointer>.allocate(capacity: 1)
    unsafePtr.pointee = metadataPtr
    $0.assign(from: UnsafePointer<Any.Type>.init(OpaquePointer(unsafePtr)),count: 1)
}
print(placeholderTypeVar)
print(type(of: structSwiftValue))

输出:

NSObject
TestStruct
__SwiftValue

中间一个是答案。而且,它与type(of: ourStruct)到内存级别的结果相同(内部产生的Metadata *等效项是相同的地址!)。

了解有关元数据的更多信息,这是Swift中一个非常有趣的话题:
https://medium.com/ios-os-x-development/types-and-meta-types-in-swift-9cd59ba92295 https://kateinoigakukun.hatenablog.com/entry/2019/03/22/184356 https://medium.com/@weswickwire/creating-a-swift-runtime-library-3cc92fc486cc

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...