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

Swift 4中使用带Self的协议模拟存在类型的实现方案问询

在Swift 4中处理带Self约束协议的集合存储问题

你遇到的是Swift里很常见的一类场景:带Self关联约束的协议无法直接作为存在类型使用。因为Bla协议里的compare(other: Self)方法要求参数必须和调用者是完全相同的类型,Swift编译器没法为这类协议生成通用的存在类型(也就是不能直接声明[Bla]数组)——它无法在运行时保证调用compare时的类型匹配,所以直接写var array: [Bla]!会编译失败。

下面给你两种Swift 4下可行的解决方案:

方案一:类型擦除(Type Erasure)

这是Swift处理这类问题的标准方案,我们可以创建一个类型擦除的包装器AnyBla,把遵循Bla的实例的行为封装起来,同时隐藏具体的类型信息:

import UIKit

protocol Bla {
    func compare(other: Self)
}

extension CGFloat : Bla {
    func compare(other: CGFloat) {
        print("Extended CGFloat")
    }
}

extension UIEdgeInsets : Bla {
    func compare(other: UIEdgeInsets) {
        print("Extended UIEdgeInsets")
    }
}

// 类型擦除包装器
struct AnyBla {
    private let _compare: (Any) -> Void
    
    init<T: Bla>(_ value: T) {
        // 捕获具体类型的compare方法,添加类型检查保证安全
        _compare = { other in
            guard let typedOther = other as? T else {
                print("类型不匹配,无法执行比较")
                return
            }
            value.compare(other: typedOther)
        }
    }
    
    // 对外暴露统一的compare方法
    func compare(other: Any) {
        _compare(other)
    }
}

// 现在可以创建存储AnyBla的数组了
class Success {
    var array: [AnyBla] = []
}

// 测试代码
let list = Success()
let f: CGFloat = 1.23
let edge = UIEdgeInsets()

list.array.append(AnyBla(f))
list.array.append(AnyBla(edge))

// 验证功能
list.array[0].compare(other: CGFloat(4.56)) // 输出 "Extended CGFloat"
list.array[0].compare(other: edge) // 输出 "类型不匹配,无法执行比较"
list.array[1].compare(other: UIEdgeInsets(top: 1, left: 2, bottom: 3, right: 4)) // 输出 "Extended UIEdgeInsets"

这种方案的好处是完整保留了Bla协议的核心逻辑,同时允许你在同一个数组中存储不同类型的实例,类型检查也能保证调用compare时的安全性。

方案二:放宽协议约束(仅业务允许时使用)

如果你的业务场景可以接受compare方法的参数不再严格限制为Self,可以修改协议的定义,把参数类型改为Bla:

import UIKit

protocol Bla {
    func compare(other: Bla)
}

extension CGFloat : Bla {
    func compare(other: Bla) {
        guard let otherFloat = other as? CGFloat else {
            print("无法和非CGFloat类型比较")
            return
        }
        print("Extended CGFloat")
    }
}

extension UIEdgeInsets : Bla {
    func compare(other: Bla) {
        guard let otherEdge = other as? UIEdgeInsets else {
            print("无法和非UIEdgeInsets类型比较")
            return
        }
        print("Extended UIEdgeInsets")
    }
}

// 现在直接声明[Bla]数组即可编译通过
class Success {
    var array: [Bla] = []
}

// 测试代码
let list = Success()
let f: CGFloat = 1.23
let edge = UIEdgeInsets()

list.array.append(f)
list.array.append(edge)

这种方案的优势是实现简单,但会失去Self约束带来的编译期类型安全性,调用compare时需要手动做类型转换,适合业务上允许跨类型比较提示的场景。

总结

如果需要严格保留Bla协议的Self约束逻辑,类型擦除方案是Swift 4下最推荐的解决方式;如果业务可以接受放宽约束,修改协议定义会更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:03:59