2017-09-15 15 views
7

Mam następujący kod:Zmuszony do oddania, nawet jeśli protokół wymaga dany rodzaj

import UIKit 

protocol Fooable: class where Self: UIViewController { 
    func foo() 
} 

class SampleViewController: UIViewController, Fooable { 

    func foo() { 
     print("foo") 
    } 
} 

let vc1: Fooable = SampleViewController() 
let vc2: Fooable = SampleViewController() 


// vc1.show(vc2, sender: nil) - error: Value of type 'Fooable' has no member 'show' 

// (vc1 as! UIViewController).show(vc2, sender: nil) - error: Cannot convert value of type 'Fooable' to expected argument type 'UIViewController' 

(vc1 as! UIViewController).show((vc2 as! UIViewController), sender: nil) 

skomentował linie nie skompilować.

Dlaczego jestem zmuszony do oddania obiektu do protokołu typu UIViewController nawet jeśli protokół Fooable wymaga, że ​​typy, które są zgodne z nim dziedziczyć UIViewController?

Odpowiedz

3

Przyjęcie protokołu Fooable informuje kompilator, że ten konkretny UIViewController reaguje na foo(), nie mniej nie więcej.

W odwrotnym wniosku Fooable ma nie zostać UIViewController koniecznie.

Ograniczenie Self: UIViewController to tylko kolejna informacja dla kompilatora narzekać w czasie kompilacji jeśli klasa dotknięte nie UIViewController

jest w twoim przypadku, gdy adnotacji SampleViewController do Fooable kompilator wie tylko, że SampleViewController reaguje na foo(). Nie wie, że typ jest faktycznie podklasą UIViewController.

Więc nie adnotuj konkretnej klasy do protokołu, jeśli chcesz uzyskać dostęp do właściwości konkretnej klasy.

Jednak można dodać metodę show i innych wspólnych właściwości/metod protokołu

protocol Fooable: class where Self: UIViewController { 
    func foo() 
    func show(_ vc: Fooable, sender: Any?) 
} 

następnie można użyć Fooable ponieważ kompilator wie, że rodzaj przyjęcia protokołu odpowiada metodzie.


Odpowiednia praktyka adnotacji typu do protokołu jest na przykład, gdy idziesz do tworzenia niejednorodnej ale Restricted typu kolekcji

let array : [CustomStringConvertible] = ["Foo", 1, false] 
array.forEach{ print("\($0)")} 

Kod drukuje trzy pozycje za pomocą właściwości description który wszystkie przedmioty odpowiadają. Kompilator rozpoznaje trzy elementy jako typy , które mają właściwość description, a nie jako String,i Bool.

-1

Nie trzeba rzutować na obiekt typu Fooable podczas tworzenia instancji kontrolera widoku. Następujące prace:

import UIKit 

protocol Fooable: class where Self: UIViewController { 
    func foo() 
} 

class SampleViewController: UIViewController, Fooable { 

    func foo() { 
     print("foo") 
    } 
} 

let vc1 = SampleViewController() 
let vc2 = SampleViewController() 


vc1.show(vc2, sender: nil) 

Każda klasa może implementować ten protokół, ale tylko UIViewController będzie miała metoda func foo() dostępny.

+0

tak, to działa, ponieważ 'vc1' i' vc2' są teraz typu 'SampleViewController'. Mój post jest oczywiście uproszczony, np. uważają, że te obiekty są zwracane z funkcji 'func buildVC (vcType: VCTypeEnum) -> Fooable' – zgorawski

+0

@zgorawski czy możesz dodać więcej kodu źródłowego? Bez większej ilości szczegółów bardzo trudno będzie Ci pomóc. – jbouaziz

+0

Nie, żadna klasa nie może zaimplementować tego protokołu, sprawdź to: https://ibb.co/ij1Wik – zgorawski

2

Wspólny wzór jest zrobić to tak:

protocol Fooable { 
    func foo() 
    var viewController: UIViewController 
} 

class SampleViewController: UIViewController, Fooable { 

    func foo() { 
     print("foo") 
    } 

    var viewController: UIViewController { return self } 
} 

w Swift 4 można dokonać vars typu UIViewController & Fooable. W Swift 3 użyj powyższej sztuczki.

0

Po pierwsze, wymaganie klasy jest tutaj zbędne, ponieważ protokół wymaga, aby każdy Fooable rozszerzał UIViewController, który jest klasą.

drugie, to czuje się jak jakiś nadzór ze strony Swift zespołu, ponieważ działa to mimo wszystko DoStuff wie o jej argumentów jest to, że realizują one Fooable, co sugeruje, że kod powinien po prostu pracować:

class Strawman { 
    let name: String 
    public func bar(_ x: Strawman) { 
     print("\(name) bars \(x.name) from entering.") 
    } 
    public init(name: String) { 
     self.name = name 
    } 
} 

protocol Fooable where Self: Strawman { 
    func foo() 
} 

class StrawFooable: Strawman, Fooable { 
    public func foo() { print("Foo!") } 
} 

let sm1 = StrawFooable(name: "Strawman1") 
let sm2 = StrawFooable(name: "Strawman2") 

// This will not compile if you define doStuff as 
// func doStuff(with x: Fooable, and y: Fooable) { 
func doStuff<T: Fooable>(with x: T, and y: T) { 
    x.bar(y) 
    x.foo() 
    y.bar(x) 
    y.foo() 
} 

// This will not compile if you annotate sm1 and sm2 as Fooable. 
doStuff(with: sm1, and: sm2) 

Moja sugestia? File a bug report.

PS. Jako bonus WTF, jeśli dodasz zgodność do klasy bazowej z rozszerzeniem, kompilator ulegnie awarii! Chodzi mi o to, że nie ma sensu robić tego, ale naprawdę nie powinno to powodować awarii kompilatora.

Powiązane problemy