Swift 5.1中子类使用泛型遇opaque类型错误如何解决
报错原因
你遇到的报错是因为MyDataProtocol是带关联类型的协议(PAT,Protocol with Associated Type),这类协议不符合some不透明类型对约束的要求,同时存储属性使用some修饰时要求编译器能在编译期确定唯一的实际类型,两者叠加触发了编译错误。另外你误以为UIView子类无法声明泛型参数是常见误区,纯代码使用的UIKit类完全支持泛型定义。
可用解决方案
方案1:直接给UIView子类添加泛型参数(纯代码布局首选)
这是最简单的实现方式,不需要额外封装协议:
// 泛型UIView子类,直接持有对应类型的MyDataModel public class MyDataView<T: Codable>: UIView { public var myData: MyDataModel<T>? }
注意:这个方案的泛型View无法在Storyboard/XIB中使用,仅适合纯代码布局的场景。
方案2:移除协议的关联类型,封装泛型逻辑(兼容IB首选)
如果需要支持Storyboard/XIB,你可以把需要用到泛型参数T的逻辑全部封装到协议的方法中,不需要对外暴露关联类型:
// 无关联类型的协议,封装所有需要用到T的操作 protocol MyDataProtocol { func encodeParam() throws -> Data func updateParam(from data: Data) throws // 其他业务需要用到T的方法都可以在这里定义 } // 让MyDataModel实现协议 extension MyDataModel: MyDataProtocol { func encodeParam() throws -> Data { try JSONEncoder().encode(dataParam) } func updateParam(from data: Data) throws { dataParam = try JSONDecoder().decode(T.self, from: data) } } // View直接使用协议作为属性类型 public class MyDataView: UIView { public var myData: MyDataProtocol? }
方案3:使用类型擦除包装类(需要对外暴露具体T类型的场景)
如果你确实需要在View外部获取具体类型的dataParam,可以实现类型擦除包装类来消除关联类型的约束:
// 类型擦除包装类 struct AnyMyData<T: Codable>: MyDataProtocol { private let getParam: () -> T private let setParam: (T) -> Void init<U: MyDataProtocol>(_ origin: U) where U.T == T { getParam = { origin.dataParam } setParam = { origin.dataParam = $0 } } var dataParam: T { get { getParam() } set { setParam(newValue) } } } // 可以选择泛型View持有擦除后的类型,也可以根据业务固定T的类型 public class MyDataView<T: Codable>: UIView { public var myData: AnyMyData<T>? }
内容的提问来源于stack exchange,提问作者Deepak Sharma
相关产品推荐
相关产品推荐

