在扩展中对Swift协议类/AnyObject的限制

4

我有两种协议,一种为对象分配了一个 serverId ,另一种为其分配了一个 status (即它是否已发布到服务器)。 这两个协议允许我处理我的服务器模型和Core Data之间的同步。

/// By conforming to this protocol, classes AND struct have an identifiable serverid `Int64`
protocol RemoteObject {
    var serverId: ServerId { get set }
}

/// This can only be applied to class
protocol Syncable: class {
    var syncStatus: SyncStatus { get set }

}

enum SyncStatus: Int64 {
    case published
    case local
}

它背后的思想是,可以将RemoteObject应用于结构体(即从服务器获取的JSON结构)和类(NSManagedObject)。而Syncable只能应用于类(NSManagedObject)。
对我来说,下一步是当syncStatus设置为.local时,我还需要通过将其设置为-1来摆脱对象上的serverId,但是尝试时出现了以下错误:
extension Syncable where Self: RemoteObject {
    var syncStatus: SyncStatus {
        get {
            SyncStatus(rawValue: syncStatusValue) ?? .local
        }
        set {
            syncStatusValue = newValue.rawValue
            if newValue == .local { serverId = -1 } //  Cannot assign to property: 'self' is immutable
        }
    }
}

无法分配给属性:'self'是不可变的

我理解这个错误是因为RemoteObject可以用于结构体,而结构体是不可变的。

然而,考虑到Syncable只能应用于类类型,难道它不会强制使RemoteObject应用于类?然后是可变的吗?

是否有一种方法可以在我的扩展中强制将RemoteObject作为类类型?例如 extension Syncable where Self: RemoteObject & class ?

1个回答

3

我最终找到了解决办法。根据这篇文章,我可以这样做:

extension Syncable where Self: RemoteObject {
    var syncStatus: SyncStatus {
        get {
            SyncStatus(rawValue: syncStatusValue) ?? .local
        }
        set {
            syncStatusValue = newValue.rawValue
            if newValue == .local { 
                var s = self
                s.serverId = -1
            } 
        }
    }
}

这样就可以解决错误,并且据我所知,这会更改引用类型的类的 serverId,而不会变异值类型的结构体(反正它们无法符合这个协议)。


进一步讲解,对于那些可能会发现这篇文章有用的人来说,我的理解是不准确的。起初,我认为问题出在可应用于任何类型的 RemoteObject 协议上。

根据这个讨论,问题似乎正好相反。当将协议限制为 :class:AnyObject 时,它会强制其 setter不可变

因此,当我试图更改 serverId 的值时,我正在从一个不可变的 setter 中调用一个可变的 setter,这导致编译器报错。对于我的使用情况,这个解决方法是可以接受的,但是为了得到一个正确的解决方案,我应该摆脱 Syncable 中的 :class,并处理迫使我首先将其限制为类的所有原因。


1
这很有趣。在 Playground 上运行它,我本来期望 var s = self 会复制 self,但当然这不是情况,因为 Syncable 总是一个类。所以,这只是为了让编译器静默。 - gcharita
1
@gcharita 我在 XCTest 中尝试了一下,它完全正常。但这仍然困扰着我,因为错误不应该出现在我的 setter 的第一个版本上,因为它只能是引用类型。当我去掉 classAnyObject 用于 Syncable 时,错误才应该出现。 - Olympiloutre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接