我正在使用 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"), accessTypeTenantKey: Optional("ROOT"), 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", netPrice: 2311.0, taxPrice: 439.0, 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", value: nil, definitionId: nil), SecureTruckParking.Attribute(key: "early_bird", value: Optional("false"), definitionId: Optional(SecureTruckParking.DefinitionId(key: "early_bird", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "manualBackofficeCancellationConfirmation", value: Optional("false"), definitionId: Optional(SecureTruckParking.DefinitionId(key: "manualBackofficeCancellationConfirmation", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "manualBackofficeCancellationConfirmationThreshold", value: Optional("1"), definitionId: Optional(SecureTruckParking.DefinitionId(key: "manualBackofficeCancellationConfirmationThreshold", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "product_icon", value: nil, definitionId: Optional(SecureTruckParking.DefinitionId(key: "product_icon", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "product_image", value: nil, definitionId: Optional(SecureTruckParking.DefinitionId(key: "product_image", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "product_tariff_group", value: nil, definitionId: Optional(SecureTruckParking.DefinitionId(key: "product_tariff_group", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "upgrading", value: Optional("false"), definitionId: Optional(SecureTruckParking.DefinitionId(key: "upgrading", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "upgrading_dates_fixed", value: Optional("false"), definitionId: Optional(SecureTruckParking.DefinitionId(key: "upgrading_dates_fixed", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "upgrading_immediate", value: Optional("false"), definitionId: Optional(SecureTruckParking.DefinitionId(key: "upgrading_immediate", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "upgrading_mail", value: Optional("false"), definitionId: Optional(SecureTruckParking.DefinitionId(key: "upgrading_mail", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "upgrading_price_surcharge", value: Optional("false"), definitionId: Optional(SecureTruckParking.DefinitionId(key: "upgrading_price_surcharge", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "early_bird_duration", value: Optional("1440"), definitionId: Optional(SecureTruckParking.DefinitionId(key: "early_bird_duration", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "recurring_max_trips", value: Optional("1"), definitionId: Optional(SecureTruckParking.DefinitionId(key: "recurring_max_trips", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "upgrading_mail_max_lead_time", value: nil, definitionId: Optional(SecureTruckParking.DefinitionId(key: "upgrading_mail_max_lead_time", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "upgrading_mail_min_lead_time", value: nil, definitionId: Optional(SecureTruckParking.DefinitionId(key: "upgrading_mail_min_lead_time", tenant: "ROOT"))), 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", value: nil, definitionId: Optional(SecureTruckParking.DefinitionId(key: "product_icon", tenant: "ROOT"))), SecureTruckParking.Attribute(key: "product_image", value: nil, definitionId: Optional(SecureTruckParking.DefinitionId(key: "product_image", tenant: "ROOT"))), 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", tenant: "ROOT"))), 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 中调用选择器的这种特定方式的详细信息可以在这里找到)
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:)
)。
我找到了一种方法来做到这一点,尽管是一种非常hacky的方法。
我们从一些虚拟对象或结构的元数据
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