我试图在一个类上添加actor isolate,但它实现了自定义的Hashable(也就是Equity).

有可能吗? 下面是一个简化的例子:

actor SomeFoo: Equatable {
    var someValue: Double = 0
    
    public static func ==(lhs: SomeFoo, rhs: SomeFoo) -> Bool {
        fabs(lhs.someValue - rhs.someValue) < 0.0001
    }

}

which yields:

在我的搜索中,我看到你可以为一个self 声明的参与者做这件事(在协议中遵循Actor,如果你有不可变的状态,这似乎是有效的).

但是在这种情况下,我需要Equatable来符合,并且我需要一个基于可变状态的equal(==)的自定义实现,这是由于我试图开始的真实代码中状态的性质.

推荐答案

一般来说,如果你试图以===以外的方式使一个演员符合Equatable,你很可能滥用了演员或Equatable.在您的特定示例中,这应该是一个值类型( struct ).行动者平等的概念,特别是依赖于可变状态,是非常有问题的,并产生了一系列的种族条件.在你测试==的时间和你使用这个事实的时间之间,任何一个参与者可能已经改变,使他们不再平等.几乎任何你想用它做的事情都应该用另一种方式来做.

在继续之前,请确保您想要Equatable.特别是,确保您的类型符合substitutability semantics of Equatable:

相等意味着可替换性——任何两个比较相等的实例可以在任何依赖于它们值的代码中互换使用.为了保持可替换性,==操作符应该考虑Equatable类型的所有可见方面.不鼓励公开Equatable类型的非值方面,而不是类标识,任何公开的方面都应该在文档中明确指出.

这通常是一个原因,它是没有意义的演员平等.你真的能,在所有情况下,用其中一个演员代替另一个演员,当他们的someValue甚至不是完全相同的值时?

考虑到所有这些,仍然值得讨论如何将其作为一个练习,即使它几乎肯定没有用处.

第一种方法是使演员不可改变.如果someValuelet,而不是var,那么一切都很好.

当然,如果someValuelet,这可能不会是一个演员,所以让我们转移到困难的情况.对于两个可变的参与者来说,符合等价的.

要做到这一点,你必须拍摄演员的快照.这是我通常用途:

import os

actor SomeFoo: Equatable {
    struct State {
        var someValue: Double = 0
    }

    private let state = OSAllocatedUnfairLock(initialState: State())

    nonisolated var snapshot: State {
        get { state.withLock { $0 } }
        set { state.withLock { $0 = newValue } }
    }

    public static func ==(lhs: SomeFoo, rhs: SomeFoo) -> Bool {
        fabs(lhs.snapshot.someValue - rhs.snapshot.someValue) < 0.0001
    }
}

你会很想创建一个nonisolated var someValue计算的getter.要非常非常小心地创建它.如果有多个可变属性(通常有),这会产生竞争条件,使多个获取可能不同步.例如,假设你写了这样的话:

// This is a very dangerous Actor.
actor SomeFoo {
    struct State {
        var firstName: String = ""
        var lastName: String = ""
    }
    
    private let state = OSAllocatedUnfairLock(initialState: State())
    
    nonisolated var snapshot: State {
        get { state.withLock { $0 } }
        set { state.withLock { $0 = newValue } }
    }
    
    // These are terrible ideas; don't do this.
    nonisolated var firstName: String {
        get { snapshot.firstName }
        set { snapshot.firstName = newValue }
    }
    nonisolated var lastName: String {
        get { snapshot.lastName }
        set { snapshot.lastName = newValue }
    }
}

这是每个人都认为他们想要的,但这是乞求创造种族条件.想象一下有什么东西在读:

if obj.firstName == "John" && obj.lastName == "Doe" { ... }

与此同时,另一个东西在写:

obj.firstName = "John"
obj.lastName = "Smith"

这些可能会交错并创建竞争条件,这正是您使用actor来避免的.在设计你的演员时,你需要非常仔细地考虑哪些值是原子的.

可变引用类型通常不应该是Equatable.

Swift相关问答推荐

如何将趋势线添加到线性图中?

循环字典中的数组

DispatchQueue.main.asyncAfter 等同于 Swift 中的 struct 化并发?

允许为 self 分配新的 struct 值的理由是什么?

为什么在Swift中每次调用Decimal类型的formatted()方法都会越来越慢?

Swift-SceneKit-无法从art.scnassets加载.scn文件

您是否必须手动指定您的 DispatchQueue 是串行的?

如何在 SwiftUI 中获取视图的位置

如何 for each 用户制作一个单独的按钮,以便在按下它时切换 isFollowingUser Bool 并更改为关注?

试图同时实现两个混合过渡

如何自己快速编写 Hashable 协议

如果没有标记,则为 Swift 预处理器

Swift 覆盖实例变量

即使设置为从不也可以访问 iOS11 照片库

在 Swift 中将计时器标签格式化为小时:分钟:秒

无法停止 AVPlayer

Swift if 语句 - 多个条件用逗号分隔?

如何在 Swift 中使用 Crashlytics 登录?

在 xcode8 中找不到 ModuleName-Swift.h 文件

如何快速格式化用户显示(社交网络等)的时间间隔?