Swift 总是因为恼人的错误信息困扰你吗?总是关于关联类型需求的问题。比如你想把某个东西转换为一个协议,但是......
别担心。现在介绍 AssociatedTypeRequirementsKit 🤗,一个 µFrameworks 的集合,可以帮助你解决这些恼人的场景!
我们之前的例子可以这样处理:
import AssociatedTypeRequirementsVisitor
private let hasher = AnyHasher()
func hashValue(value: Any) -> Int {
// There's a function `AnyHasher.callAsFunction(_:Any) -> Int?`
return hasher(value) ?? 0
}
struct AnyHasher: HashableVisitor {
// This function will called by `callAsFunction(_:Any)` if the input conforms to hashable
func callAsFunction<T : Hashable>(_ value: T) -> Int {
return value.hashValue
}
}
你可以通过 Swift 包管理器 安装 AssociatedTypeRequirementsKit,只需将以下行添加到你的 Package.swift
文件中:
import PackageDescription
let package = Package(
[...]
dependencies: [
.package(url: "https://github.com/nerdsupremacist/AssociatedTypeRequirementsKit.git", from: "0.1.0")
]
)
如果你想在一个带有关联类型的协议上调用一个函数,那么你必须提供一个泛型函数。由于闭包不能是泛型的,我们必须使用一个协议来编码它。
例如,如果你想将任何 SwiftUI 视图转换为 AnyView,但在编译时不知道类型,你可以使用 ViewVisitor
。
import AssociatedTypeRequirementsVisitor
import SwiftUI
private let converter = AnyViewConverter()
extension AnyView {
init?(_ value: Any) {
guard let view = converter(value) else { return nil }
self = view
}
}
private struct AnyViewConverter : ViewVisitor {
// Provide a function that can be called with all the necessary type information
func callAsFunction<T : View>(_ value: T) -> AnyView {
return AnyView(value)
}
}
但是你为什么要这样做呢? 例如,如果你想获取元组视图的所有子视图呢?
extension TupleView {
func subviews() -> [AnyView] {
let mirror = Mirror(reflecting: self)
let tuple = mirror.children.first!.value
let tupleMirror = Mirror(reflecting: tuple)
return tupleMirror.children.map { AnyView($0.value)! }
}
}
ViewVisitor
是开箱即用的,因为我们已经在为 Swift 中最重要的有问题的协议提供 visitor 协议,并且正在不断扩展列表。 如果你需要处理自己的协议,你可以按照以下示例进行操作:
protocol MyProtocolVisitor: AssociatedTypeRequirementsVisitor {
associatedtype Visitor = MyProtocolVisitor
associatedtype Input = MyProtocol
associatedtype Output
func callAsFunction<T : MyProtocol>(_ value: T) -> Output
}
如果你不想使用 AssociatedTypeRequirementsVisitor
API,你也可以使用底层的 withCasted
API 并自行请求协议一致性。
import Casting
func test(value: Any) -> AnyHashable? {
return withCasted(value, as: .hashable) { casted in
// casted is CastedProtocolValue
...
}
}
但这个 CastedProtocolValue
是什么呢? 这是一个小结构体,它具有与函数 func anyHashable<T : Hashable>(hashable: T)
期望的相同的布局。 所以该函数可以被强制转换。
import Casting
func test(value: Any) -> AnyHashable? {
return withCasted(value, as: .hashable) { casted in
// A pointer to the function is in `functionPointer`
let function = unsafeBitCast(functionPointer, (@convention(thin) (CastedProtocolValue) -> AnyHashable).self)
return function(casted)
}
}
当你使用 Swift 标准库中的 withUnsafePointer
API,但使用 Any
时,你会注意到你获得的指针或字节不太正确。 这是因为它们指向存在性容器 Any
,它总是 32 字节。
这就是为什么我们提供 withUnsafeValuePointer
,它将始终指向实际值,而不是容器。
import ValuePointers
struct MyStruct {
let first: String
let second: String
}
let value = MyStruct(first: "A", second: "B") as Any
let secondString = withUnsafeValuePointer(to: value) { $0.assumingMemoryBound(to: String.self).advanced(by: 1).pointee }
// "B"
print(secondString)
每当你想访问带有关联类型的协议的元类型时,你都会遇到完全相同的问题。 你可以使用 ProtocolType
通过协议名称来访问它。
import ProtocolType
let protocolType = ProtocolType(moduleName: "SwiftUI", protocolName: "View")
print(protocolType?.type) // SwiftUI.View.self
ProtocolType 提供了已经提供的常量集合。 常量列表可以在 commonProtocols.json
文件中更改,并且可以进一步扩展。
import ProtocolType
func hash(protocol protocolType: ProtocolType) -> some Hashable {
return unsafeBitCast(type, as: Int.self)
}
let hashed = hash(protocol: .collection)
欢迎并鼓励贡献!
AssociatedTypeRequirementsKit 在 MIT 许可证下可用。 有关更多信息,请参阅 LICENSE 文件。