2017-01-03 7 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

Связанный: [Swift Протокол наследования и протокол conformence выпуск] (Http: // StackOverflow.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!' To 'ParentImpl 'для удовлетворения требования протокола (а затем ваше фактическое свойство должно быть типа' ChildImpl! '). – Hamish

+0

@ Хамиш, я тоже оценил этот подход, но, как вы сказали, это не очень приятно, и для этого требуется поддерживать два свойства с одинаковой ролью :( – 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

Спасибо за ваш вклад, к сожалению, дженерики также недоступны в объективе-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

Вы работаете на вас?

EDIT: Я только заметил, что этот вопрос был опубликован более 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 показывая, что вы уже пробовали, используя соответствующий тип или отдельное свойство только для выполнения протокола соответствия. Я скоро Swift скоро поддержу выведенный тип из составленных типов, таких как let child: Child & ChildImpl = ChildImpl() или просто child: ChildImpl с ChildImpl is Child. Но до тех пор я подумал, что предлагаю еще одну альтернативу, которая заключается в том, чтобы отделить 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()' public, и я хочу сохранить это внутреннее для кластер из двух классов. Вот почему в моем коде 'doSomething' является частью класса реализации. – Cristik

+0

@Cristik Возможно, вы можете добавить api в частное расширение 'ChildTemporaryBase'. Я отредактировал свой ответ с чем-то подобным, это определенно больше кода, чем использование вычисленного свойства, но, я думаю, он сохраняет «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" 

А вот типа 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" 
+0

К сожалению, я не могу использовать связанные значения, и не набирать стиратели с дженериками, так как мне нужно взаимодействовать с Objective-C ... – Cristik

+0

@Cristik Эта тема на самом деле очень старая и обсуждалась на многие блоги и другие вопросы. Если интерфейс objc важен для вас, у вас нет другого выбора, несите с уродливым решением :(Я должен был использовать тот же механизм для проекта, и, честно говоря, это не «что» плохо, но хорошо, вы всегда заметите тот факт, что человек этот код меня раздражает! Такие вещи являются одной из причин, по которым я не буду нанимать или работать в компании, которая все еще кодирует Obj-c. Если вы ищете 'Swift protocol covariance', – farzadshbfn

+0

Кроме того, если возможно, вы можете определить 'Child' и' Parent' как абстрактные классы. IK swift не имеет такого термина, но вы можете использовать некоторые работы, такие как 'fatalError (« реализация в подклассе ») или подобные вещи , – farzadshbfn

 Смежные вопросы

  • Нет связанных вопросов^_^