我无法使用以下代码:
@objc protocol Child { }
@objc protocol Parent {
var child: Child { get }
}
class ChildImpl: Child {
func doSomething() { }
}
class ParentImpl: Parent {
let child = ChildImpl()
// this would solve the problem, however can't access the ChildImpl members
// that are not part of the protocol
// let child: Child = ChildImpl()
// as well as this, however maintaining two properties is an ugly hack
// var child: Child { return childImpl }
// private let childImpl = ChildImpl()
}
我得到的错误:
Playground执行失败:错误:Tests.playground:3:7:错误:类型'ParentImpl'不符合协议'Parent' class ParentImpl:Parent { ^
Tests.playground:4:9:注意:候选人有不匹配类型'ChildImpl' var child = ChildImpl()
基本上我有两个父子协议,以及两个实现这两个协议的类。但是,编译器仍然抱怨ChildImpl
不是Child
,这不是真的。
如果我在Parent
protocol Parent {
associatedtype ChildType: Child
var child: ChildType { get }
}
,但我需要将协议提供给Objective-C,并且还需要能够引用child
作为实际的具体类型。
是否有一个解决方案,不涉及重写Objective-C
中的协议,或者不添加重复的属性声明只是为了避免这个问题?
答案 0 :(得分:2)
我在评论中提到了link,显示了您尝试过的内容,使用关联类型或单独属性来完成协议一致性。我的事情Swift将很快支持来自let child: Child & ChildImpl = ChildImpl()
等组合类型的推断类型,或仅child: ChildImpl
,因为ChildImpl
是Child
。但在此之前,我认为我建议另外一种方法是将ChildImpl
所需的apis分开,并将它们放在Child
继承的单独协议中。这样,当Swift编译器支持此功能时,您不需要重构,只需将其删除即可。
// TODO: Remove when Swift keeps up.
@objc protocol ChildTemporaryBase { }
private extension ChildTemporaryBase {
func doSomething() {
(self as? ChildImpl).doSomething()
}
}
@objc protocol Child: ChildTemporaryBase { }
class ParentImpl: Parent {
let child: Child = ChildImpl()
func testApi() {
child.doSomething?()
}
}
答案 1 :(得分:2)
您尝试执行的操作称为covariance
,而swift不支持符合这些协议的协议或类/结构中的协方差。
您必须使用Type-Erassure
或associatedTypes
:
protocol Child { }
protocol Parent {
associatedtype ChildType: Child
var child: ChildType { get }
}
class ChildImpl: Child {
func doSomething() {
print("doSomething")
}
}
class ParentImpl: Parent {
typealias ChildType = ChildImpl
let child = ChildImpl()
func test() {
child.doSomething()
}
}
ParentImpl().test() // will print "doSomething"
这里是Parent
协议的一般用法的Type-Erased Parent:
struct AnyParent<C: Child>: Parent {
private let _child: () -> C
init <P: Parent>(_ _selfie: P) where P.ChildType == C {
let selfie = _selfie
_child = { selfie.child }
}
var child: C {
return _child()
}
}
let parent: AnyParent<ChildImpl> = AnyParent(ParentImpl())
parent.child.doSomething() // and here in Parent protocol level, knows what is child's type and YES, this line will also print "doSomething"
答案 2 :(得分:0)
如果您不介意将扩展属性添加到ParentImpl类:
@objc protocol Child {}
@objc protocol Parent {
var child: Child! { get }
}
class ChildImpl: Child { }
class ParentImpl: Parent {
var child: Child!
}
extension ParentImpl {
convenience init(child: Child?) {
self.init()
self.child = child
}
var childImpl: ChildImpl! {
get { return child as? ChildImpl }
set { child = newValue }
}
}
let parent = ParentImpl(child: ChildImpl())
let child = parent.child
答案 3 :(得分:0)
您的ParentImpl类没有子类型协议。我解决了这个问题。
class ParentImpl: Parent {
var child: Child = ChildImpl()
}
答案 4 :(得分:0)
我发现了一个几乎不错的解决方案,现在有了Swift 5.1,就是使用属性包装器:
@propertyWrapper
struct Hider<T, U> {
let wrappedValue: T
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
var projectedValue: U { return wrappedValue as! U }
}
@objcMembers class ParentImpl: NSObject, Parent {
@Hider<Child, ChildImpl> var child = ChildImpl()
}
通过这种方式,child
被公开为Child
,而$child
被公开为ChildImpl
,从而允许从ParentImpl
内部使用非协议成员。
该解决方案并不理想,因为我还找不到一种方法来描述T
应该是U
的超类型。