Swift中带required初始化方法的协议类型擦除实现方案
解决方法
你的问题核心在于,泛型版本的AnyTestProtocol需要实现协议新增的required init(id: Int),而因为T遵循TestProtocol,它必然已经实现了这个初始化方法,我们可以直接在AnyTestProtocol中调用T的初始化逻辑来创建底层实例。
修改后的AnyTestProtocol实现
class AnyTestProtocol<T: TestProtocol>: TestProtocol { var id: Int { return item.id } private let item: T // 从已有实例初始化的方法保持不变 init(_ testProtocol: T) { self.item = testProtocol } // 实现协议要求的required初始化方法 required init(id: Int) { // 利用T遵循TestProtocol的特性,直接调用其init(id:) self.item = T(id: id) } }
测试验证
用你的TestStruct来验证功能是否正常:
struct TestStruct: TestProtocol { let id: Int // 结构体自动合成了init(id: Int),满足协议要求 } // 测试通过required初始化创建实例 let any1 = AnyTestProtocol<TestStruct>(id: 1) let any2 = AnyTestProtocol<TestStruct>(id: 1) print(any1 == any2) // 输出 true let any3 = AnyTestProtocol<TestStruct>(id: 2) print(any1 == any3) // 输出 false // 测试从已有实例初始化 let testStruct = TestStruct(id: 3) let any4 = AnyTestProtocol(testStruct) print(any4.id) // 输出 3
进阶:真正的非泛型类型擦除(可选)
如果你需要真正的类型擦除(比如把不同TestProtocol实现的实例存入同一个数组),上面的泛型版本就不够用了。这时候需要将AnyTestProtocol改为非泛型类,通过存储闭包封装初始化、相等性判断等逻辑:
class AnyTestProtocol: TestProtocol { private let underlyingInstance: Any private let _id: () -> Int private let _equals: (AnyTestProtocol) -> Bool var id: Int { return _id() } init<T: TestProtocol>(_ instance: T) { self.underlyingInstance = instance self._id = { instance.id } self._equals = { other in guard let otherTypedInstance = other.underlyingInstance as? T else { return false } return instance == otherTypedInstance } } required init(id: Int) { // 非泛型初始化无法确定具体类型,直接调用会报错 // 建议通过具体类型的实例创建AnyTestProtocol后再使用相关功能 fatalError("请通过`AnyTestProtocol<T>(_:)`初始化实例,不要直接调用此方法") } // 重写==运算符以支持AnyTestProtocol的相等性判断 static func == (lhs: AnyTestProtocol, rhs: AnyTestProtocol) -> Bool { return lhs._equals(rhs) } }
使用示例:
struct TestStruct1: TestProtocol { let id: Int } struct TestStruct2: TestProtocol { let id: Int } let array: [AnyTestProtocol] = [ AnyTestProtocol(TestStruct1(id: 1)), AnyTestProtocol(TestStruct2(id: 1)) ] print(array[0] == array[1]) // 输出 false,底层类型不同 print(array[0] == AnyTestProtocol(TestStruct1(id: 1))) // 输出 true
总结
- 若只需绑定到具体
TestProtocol实现类型的泛型封装,直接在AnyTestProtocol<T>的required init中调用T(id: id)即可。 - 若需要混合存储不同实现类型的非泛型类型擦除,需通过闭包封装核心逻辑,并注意
required init的调用限制。
内容的提问来源于stack exchange,提问作者Vladislav
相关产品推荐
相关产品推荐

