问题描述
我在绕圈,试图让Hashable
使用符合相同protocol
的多个struct
。
我有一个协议SomeLocation
声明如下:
protocol SomeLocation {
var name:String { get }
var coordinates:Coordinate { get }
}
然后我创建多个包含类似数据的对象:
struct ShopLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
init(from decoder: Decoder) throws {
...
}
}
struct CarLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
init(from decoder: Decoder) throws {
...
}
}
稍后我可以在同一数组中使用它们,方法是声明:
let locations: [SomeLocation]
问题是,我创建了一个MKAnnotation
子类,并且需要对SomeLocation
对象使用自定义的Hashable
。
final class LocationAnnotation:NSObject, MKAnnotation {
let location:SomeLocation
init(location:SomeLocation) {
self.location = location
super.init()
}
}
override var hash: Int {
return location.hashValue
}
override func isEqual(_ object: Any?) -> Bool {
if let annot = object as? LocationAnnotation
{
let isEqual = (annot.location == location)
return isEqual
}
return false
}
这给了我两个错误:
所以我将Hashable
协议添加到我的SomeLocation
协议:
protocol SomeLocation: Hashable {
...
}
这消除了hashValue不可用的第一个错误,但现在我在声明let location:SomeLocation
说
因此看起来我无法将Hashable
添加到协议。
我可以将Hashable
直接添加到实现SomeLocation
协议的每个结构中,但是这意味着我需要使用这样的代码,并在每次可能创建另一个符合SomeLocation
协议的对象时不断更新它。
override var hash: Int {
if let location = location as? ShopLocation
{
return location.hashValue
}
return self.hashValue
}
我尝试了另一种方法,通过创建SomeLocationRepresentable
struct:
struct SomeLocationRepresentable {
private let wrapped: SomeLocation
init<T:SomeLocation>(with:T) {
wrapped = with
}
}
extension SomeLocationRepresentable: SomeLocation, Hashable {
var name: String {
wrapped.name
}
var coordinates: Coordinate {
wrapped.coordinates
}
func hash(into hasher: inout Hasher) {
hasher.combine(name)
hasher.combine(coordinates)
}
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.name == rhs.name && lhs.coordinates == rhs.coordinates
}
}
但是,当我尝试在LocationAnnotation
类(如
let location: SomeLocationRepresentable
init(location:SomeLocation) {
self.location = SomeLocationRepresentable(with: location)
super.init()
}
我收到错误
我想做的事情有可能实现吗?是否使用全部符合协议的对象并使用自定义Hashable
进行相互比较?
推荐答案
从Hashable
派生协议并使用类型橡皮擦在这里可能会有所帮助:
protocol SomeLocation: Hashable {
var name: String { get }
var coordinates: Coordinate { get }
}
struct AnyLocation: SomeLocation {
let name: String
let coordinates: Coordinate
init<L: SomeLocation>(_ location: L) {
name = location.name
coordinates = location.coordinates
}
}
然后您可以简单地在结构上声明协议一致性,如果Coordinate
已经是Hashable
,那么您不需要编写任何额外的散列代码,因为编译器可以自动为您合成(只要新类型的所有属性都是Hashable
:
struct ShopLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
}
struct CarLocation: SomeLocation, Decodable {
var name: String
var coordinates: Coordinate
}
如果Coordinate
也是Codable
,那么您也可以省略编写编码/解码操作的任何代码,编译器将综合所需的方法(假设所有其他属性都已经Codable
)。
然后可以通过转发初始值设定项约束在注释类中使用橡皮擦:
final class LocationAnnotation: NSObject, MKAnnotation {
let location: AnyLocation
init<L: SomeLocation>(location: L) {
self.location = AnyLocation(location)
super.init()
}
override var hash: Int {
location.hashValue
}
override func isEqual(_ object: Any?) -> Bool {
(object as? LocationAnnotation)?.location == location
}
}
这篇关于使快速协议符合Hasable的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!