2017-01-03 51 views
9

我不能让下面的代码工作:亲子关系协议和ObjC可用性

@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 execution failed: error: Tests.playground:3:7: error: type 'ParentImpl' does not conform to protocol 'Parent' class ParentImpl: Parent { ^

Tests.playground:4:9: note: candidate has non-matching type 'ChildImpl' var child = ChildImpl()

基本上我有两个亲子协议,和两个实现这两个协议的类。但编辑仍然抱怨ChildImpl不是Child,这是不正确的。

我可以让错误消失,如果我对Parent

protocol Parent { 
    associatedtype ChildType: Child 
    var child: ChildType { get } 
} 

使用关联的类型,但我需要有可用的Objective-C的协议,同时还需要能够引用child为实际的具体类型。

是否有解决方案,不涉及重写Objective-C中的协议,或者不添加重复的属性声明只是为了避免该问题?

+0

相关:[斯威夫特协议继承和协议conformence问题( HTTP://计算器。com/questions/40410884/swift-protocol-inheritance-and-protocol-conformence-issue)(另请参阅:[协议不符合自身吗?](http://stackoverflow.com/questions/33112559/protocol-doesnt -conform到本身))。 – dfri

+0

请参见[本问答](http://stackoverflow.com/q/42561685/2976878) - 在您的案例中,一种可行(但不是特别好)的解决方案是将“Child!”类型的虚拟属性定义为“ParentImpl '以满足协议要求(然后让您的实际属性为'ChildImpl!'类型)。 – Hamish

+0

@Hamish,然后我评估了这种方法,但是(正如你所说)它不是很好,它需要保持两个属性具有相同的作用:( – Cristik

回答

1

如果你不介意将扩展属性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 
+0

@Cristik只是想法如何实现你的想法,而不是实际解决你的问题。 – bubuxu

+0

感谢您的意见,不幸的是,仿制药在Objective-C中也不可用。 – Cristik

+0

现在删除仿制药。 – bubuxu

1

你ParentImpl类有没有孩子类型的协议。我解决了这个解决方案。

class ParentImpl: Parent { 
    var child: Child = ChildImpl() 
} 
+0

我想从'ChildImpl'中调用方法而不用强制属性,我该怎么做? – Cristik

+0

我更新了与上述细节的问题,对不起,如果之前不明确 – Cristik

+0

@Cristik我认为你不能调用'ChildImpl'的方法,而没有铸造子 –

1

这对你有帮助吗?

protocol Child { } 

protocol Parent { 
    var child: Child { get } 
} 

class ChildImpl: Child { 
    func doSomething() { } 
} 

class ParentImpl: Parent { 
    var child: Child { get { return ChildImpl() } } 
} 
+0

有了这个,我将无法访问不属于该协议的'ChildImpl'成员。此外,每次查询属性时都会创建一个新的'ChildImpl'实例(尽管这可以通过'lazy'部分解析) – Cristik

-1

这样做对你有用吗?

编辑:我刚刚注意到这个问题发布超过1年前。它此刻虽然热门问题...

//: Playground - noun: a place where people can play 

import UIKit 

@objc protocol Child { 
    func eat() 
} 

@objc protocol Parent: Child { 
// init() 
    func buyFood() 
} 

class Human {} 

class Person: Human, Parent { 

// required override init() {} 

    func buyFood() {} 

    func eat() {} 
} 

class Nursery { 

    let person: Human & Parent 

    init() { 
    person = Person() 
    } 

    func dinnerBell() { 
    person.buyFood() 
    person.eat() 
    } 
} 

//class School<T: Parent> { 
// 
// let parent = T() 
// 
// func dinnerBell() { 
// parent.buyFood() 
// parent.eat() 
// } 
//} 

class University { 

    let person = Person() 

    func dinnerBell() { 
    person.buyFood() 
    person.eat() 
    } 
} 
+0

添加一些说明 – Billa

2

我提到在评论一个link显示你已经尝试过的东西,使用相关的类型或单独的属性只是履行协议一致性。我的事情斯威夫特很快将支持从组合类型推断类型,如let child: Child & ChildImpl = ChildImpl()或简单地child: ChildImpl因为ChildImplChild。但在此之前,我认为我建议另外一种选择,即将您需要的apis从ChildImpl中分离出来,并将它们放入一个单独的协议中,以便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?() 
    } 
} 
+0

这使得'doSomething()'公开,并且我希望将此内部保留为两类的聚类。这就是为什么在我的代码中'doSomething'是实现类的一部分。 – Cristik

+0

@Cristik也许你可以在'ChildTemporaryBase'私有扩展中添加api。我用类似的方式编辑了我的答案,这绝对比使用计算属性更多的代码,但是我想它保持'ParentImpl'干净。 – Lukas

+0

有趣的做法,但似乎我将不得不添加大量的样板代码,并且当方法返回值时,事情变得复杂,因为我需要“保护”或强制解包结果 – Cristik

2

你正在试图做的,什么叫covariance快捷不支持符合这些协议的协议或类/结构的协方差。 你要么必须使用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协议的一般使用的类型擦除家长:

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" 
+0

不幸的是,我不能使用关联的值,也不能使用泛型类型的橡皮擦,因为我需要与Objective-C进行交互... – Cristik

+0

@Cristik这个主题实际上已经很老了,在许多博客和其他SO问题上。如果objc接口对你很重要,你没有别的选择,忍受丑陋的解决方案:(我必须使用相同的机制进行项目,说实话这不是“那个”坏,但是,你会一直注意到事实上这个代码会让我很恼火!像这样的事情,是我不会在仍然使用Obj-c编码的公司中招聘或工作的原因之一。如果搜索Swift协议协议,所有主题都会弹出, – farzadshbfn

+0

另外,如果可能的话,你可以将'Child'和'Parent'定义为抽象类,IK swift没有这样的术语,但是你可以使用一些解决方法,如'fatalError(“子类中的实现”)或类似的东西。 – farzadshbfn