You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中非final类遵守含Self的协议报错,空协议扩展为何可行?

为什么协议要求与协议扩展方法在非final类上表现差异?

我想要实现一个仅当新值与当前值不同时才更新属性的方法,先定义了如下示例模型:

class Model {
    var id: Int = 0
}

class Person: Model {
    var name: String = ""
}

class Dog: Model {
    var color: UIColor = .black
}

方案一:带协议要求的Updatable(编译报错)

首先定义包含方法要求的Updatable协议,并提供默认实现:

协议定义

protocol Updatable: AnyObject{

    @discardableResult
    func update<Value: Equatable>(
        _ keyPath: ReferenceWritableKeyPath<Self, Value>,
        to value: Value
    ) -> Bool
}

默认实现

extension Updatable {
    @discardableResult
    func update<Value: Equatable>(
        _ keyPath: ReferenceWritableKeyPath<Self, Value>,
        to value: Value
    ) -> Bool {
        guard self[keyPath: keyPath] != value else { return false }
        self[keyPath: keyPath] = value
        return true
    }
}

当让非final类Model遵守该协议时:

extension Model: Updatable {}

编译器抛出错误:

Protocol 'Updatable' requirement 'update(_:to:)' cannot be satisfied by a non-final class ('Model') because it uses 'Self' in a non-parameter, non-result type position

方案二:空协议+扩展方法的Updatable2(正常运行)

将协议改为空协议,在扩展中添加同名逻辑的方法:

协议与扩展实现

protocol Updatable2: AnyObject { }

extension Updatable2 {

    @discardableResult
    func update2<Value: Equatable>(
        _ keyPath: ReferenceWritableKeyPath<Self, Value>,
        to value: Value
    ) -> Bool {
        print("Default: \(keyPath) -> \(value)")
        guard self[keyPath: keyPath] != value else { return false }
        self[keyPath: keyPath] = value
        return true
    }
}

extension Model: Updatable2 {}

以下调用均能正常运行:

let model = Model()
model.update2(\.id, to: 200)

let person = Person()
person.update2(\.name, to: "Sally")
person.update2(\.id, to: 400)

let car = Car()
car.update2(\.wheels, to: 5)

let models: [Model] = [
    model,
    person,
    car,
]
models.forEach { $0.update2(\.id, to: 666) }

差异原因解析

核心区别在于方法是否属于协议的强制要求:

  1. 协议要求的方法:
    当方法是协议的必须实现项时,编译器需要保证该方法在所有子类中能被正确重写和兼容。这里的ReferenceWritableKeyPath<Self, Value>中,Self作为泛型参数的约束类型(键路径的根类型),对于非final类来说,子类的Self是子类类型,父类的Self是父类类型,编译器无法确保子类重写的方法能满足父类的协议要求——这种Self出现在非参数/非返回值的泛型约束位置的情况,会导致继承时的类型冲突,因此编译器禁止非final类遵守此类协议。

  2. 协议扩展中的默认方法:
    这种方法不属于协议的强制要求,只是给遵守协议的类型提供一个“附加”的实现,采用静态派发逻辑。调用时Self会绑定到具体的调用类型(比如Person或Model),编译器不需要处理子类重写的兼容性问题——即使子类继承该方法,也不会触发协议要求的类型校验,因此不会报错,且能正常工作。

内容的提问来源于stack exchange,提问作者Rob C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 14:36:20