【发布时间】:2021-02-04 15:51:51
【问题描述】:
我绕着圈子试图让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
}
这给了我 2 个错误:
“SomeLocation”类型的值没有成员“hashValue”二元运算符
'==' 不能应用于两个 'SomeLocation' 操作数
所以我将Hashable 协议添加到我的SomeLocation 协议中:
protocol SomeLocation: Hashable {
...
}
这消除了 hashValue 不可用的第一个错误,但现在我在声明 let location:SomeLocation 的地方遇到错误
Protocol 'SomeLocation' 只能用作通用约束,因为它具有 Self 或关联的类型要求
所以看起来我不能将Hashable 添加到协议中。
我可以将Hashable 直接添加到实现SomeLocation 协议的每个结构中,但这意味着我需要使用这样的代码并在每次创建另一个符合SomeLocation 协议的对象时不断更新它.
override var hash: Int {
if let location = location as? ShopLocation
{
return location.hashValue
}
return self.hashValue
}
我尝试了另一种方法,通过创建 SomeLocationRepresentable 结构:
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()
}
我收到一个错误
协议类型“SomeLocation”的值不能符合“SomeLocation”;只有结构/枚举/类类型可以符合协议
是否有可能实现我想要做的事情?使用所有符合协议的对象并使用自定义Hashable 来比较一个对象?
【问题讨论】:
-
您的问题源于在某些地方(例如,在创建数组
[SomeLocation]时)尝试将协议用作存在(即代替对象),同时还尝试遵守 Hashable ,它有Self约束,这会阻止它在 Swift 中被用作存在。类型擦除(就像您对SomeLocationRepresentable所做的那样 - Swift 中的约定是将其命名为AnyLocation)是可行的方法,因此您将拥有var locations: [AnyLocation] = [AnyLocation(CarLocation())] -
与您的问题无关,但您将如何检查它们是否在同一位置?他们可能会非常接近但不相等。顺便说一句,它们可能共享相同的纬度/经度并且在不同的地方(即不同的级别)
-
感谢@NewDev 知道为什么会尝试失败吗?
-
@LeoDabus 我已经为这个问题简化了我的代码。但是在这种情况下,它用于比较地图上的注释。因此,即使只是比较名称也可以,因为它们都是独一无二的。
-
它失败了,因为您尝试使用类型
SomeLocation初始化SomeLocationRepresentable,而它期望的类型 符合 到SomeLocation.协议不符合协议,包括它们自己。当你有Self约束(Hashable有)时,你基本上不能有SomeLocation的东西。
标签: ios swift protocols swift-protocols hashable