2015-02-24 10 views
7

Powiedzmy Stworzyłem ten protokół i kilka klasZadeklaruj tablicę klas, które są zgodne z protokołem

import UIKit 

protocol ControllerConstructorProtocol { 
    class func construct() -> UIViewController? 
} 

class MyConstructor: ControllerConstructorProtocol { 
    class func construct() -> UIViewController? { 
     return UIViewController() 
    } 
} 

class MyOtherConstructor: ControllerConstructorProtocol { 
    class func construct() -> UIViewController? { 
     return UITableViewController(style: .Grouped) 
    } 
} 

Teraz chcę zadeklarować tablicę, która zawiera klasy obiektów, które będą zgodne z takiego protokołu. Jak mogę to zgłosić? Idealnie chciałbym, aby kompilator sprawdził, czy tablica jest poprawnie wypełniona (podczas kompilacji), zamiast sprawdzać ją w (czasie pracy) z operatorem as.

To co próbowałem bezskutecznie :(

  1. Prowadzi to do błędu kompilacji:

    'Any Object does not have a member named 'construct'

    var array = [ 
        MyConstructor.self, 
        MyOtherConstructor.self, 
    ] 
    
    var controller = array[0].construct() // << ERROR here 
    
  2. Zapis ten jest jeszcze gorzej, ponieważ sama klasa robi niezgodny z protokołem (ich wystąpienia)

    Type 'MyConstructor.Type' does not conform to protocol 'ControllerConstructorProtocol'

    var array: Array<ControllerConstructorProtocol> = [ 
        MyConstructor.self, // << ERROR here 
        MyOtherConstructor.self, 
    ] 
    

EDIT 23.04.2016 W Swift 2.2 (Xcode 7.3) możliwe jest napisać @rintaro's original idea :)

let array: Array<ControllerConstructorProtocol.Type> = [ 
    MyConstructor.self, 
    MyOtherConstructor.self, 
] 
let viewController = array[0].construct() 

Odpowiedz

8

„wachlarz zajęć, które są zgodne z protokołem” może być uznane za podobne Array<TheProtocol.Type>.

Można:

var array: Array<ControllerConstructorProtocol.Type> = [ 
    MyConstructor.self, 
    MyOtherConstructor.self, 
] 

Ale ...

array[0].construct() 
//^error: accessing members of protocol type value 'ControllerConstructorProtocol.Type' is unimplemented 

Wywoływanie metody na rzeczy jest "niewykonane".

Do tej pory musisz zadeklarować protokół jako @objc i wywołać metodę przez AnyClass. Co więcej, z pewnych powodów nie możemy bezpośrednio obsadzić array[0] do AnyClass, zamiast tego musimy przesłać go do Any, a następnie AnyClass.

@objc protocol ControllerConstructorProtocol { 
    class func construct() -> UIViewController? 
} 

var array: Array<ControllerConstructorProtocol.Type> = [ 
    MyConstructor.self, 
    MyOtherConstructor.self, 
] 

let vc = (array[0] as Any as AnyClass).construct() 

Uwaga: Problem z przesyłaniem został naprawiony w Swift 1.2/Xcode 6.3.Ale „wdrożone” jest „unimplmented” :(


Wystarczy przypadkowe pomysły:

To zależy od rzeczywistego wykorzystania indywidualnie, ale w tym konkretnym przypadku, tablica ()-> UIViewController? zamknięć jest wystarczająca:

var array: [() -> UIViewController?] = [ 
    MyConstructor.construct, 
    MyOtherConstructor.construct, 
] 

let vc = array[0]() 

Jeśli masz kilka sposobów, możesz użyć typu-skasowane otoki protokołu.

protocol ControllerConstructorProtocol { 
    class func construct() -> UIViewController? 
    class func whoami() -> String 
} 

struct ControllerConstructorWrapper { 
    private let _construct:() -> UIViewController? 
    private let _whoami:() -> String 
    init<T: ControllerConstructorProtocol>(_ t:T.Type) { 
     _construct = { t.construct() } 
     _whoami = { t.whoami() } 
    } 
    func construct() -> UIViewController? { return _construct() } 
    func whoami() -> String { return _whoami() } 
} 

var array: [ControllerConstructorWrapper] = [ 
    ControllerConstructorWrapper(MyConstructor), 
    ControllerConstructorWrapper(MyOtherConstructor), 
] 

let who = array[0].whoami() 
let vc = array[0].construct() 
+0

Rozumiem. Tak więc "niezatwierdzeni" twórcy szybkich kompilatorów wciąż pracują nad poprawką. W tej chwili nie ma rozwiązania bez słowa kluczowego "as". – nacho4d

+0

Tak, wierzę, że jabłko nad tym pracuje. Uaktualniłem odpowiedź, ale uważam, że nie ma prostej metody rozwiązania tego problemu. – rintaro

+1

Teraz o tym wspomniałeś, posiadanie szeregu zamknięć jest dla mnie wystarczające! 2 り が と う ご ざ い ま し た! – nacho4d

0

Spróbuj tego:

 var myConst = MyConstructor() 
     var myOthConst = MyOtherConstructor() 

     var array:[AnyObject] = [ 
      myConst, 
      myOthConst 
     ] 

     for a in array 
     { 
      if a is MyConstructor 
      { 
       println("a is type of MyConstructor") 
       (a as! MyConstructor).myMethod() 
      } 
      else if a is MyOtherConstructor 
      { 
       println("a is type of MyOtherConstructor") 
       (a as! MyOtherConstructor).myMethod() 
      } 
     } 

Inne rozwiązanie, choć nie takie ładne ..

+1

wolałbym nie używać 'jak something' jak napisałem w pytaniu. Zastanawiam się, czy istnieje ogólny sposób robienia tego. Powiedzmy, że mam 10 elementów w tablicy ... Musiałbym napisać do wielu 'as'es) – nacho4d

+0

Problem polega na tym, że tablica jest typu AnyObject, ponieważ istnieje wiele typów klasy w tablicy, a kompilator nie robi wiem, który typ jest elementem w indeksie i. Zaktualizuję odpowiedź lepszym rozwiązaniem. –

+0

Masz rację, ale te klasy mają wspólny protokół. Chciałbym zadeklarować tablicę AnyClass, ale powinny one być zgodne z ControllerConstructorProtocol, nie wiem nawet, czy jest to możliwe. – nacho4d

0

Nie jestem pewien, czy mam pytanie całkowicie, ale dlaczego nie w ten sposób:

var array: [ControllerConstructorProtocol] = [MyConstructor(), MyOtherConstructor()] 
+0

Nie chce przechowywać instancji, ale klasy. – Alex

+0

Instancje są bardzo drogie w moim przypadku. Właśnie dlatego chcę, aby zajęcia odbywały się za pomocą wspólnej metody, która stworzy potrzebne instancje. – nacho4d

0

Jeśli naprawdę chcesz mieć tylko klasy (zgodne z protokołem), możesz to zrobić w następujący sposób (w Swift 3):

Jeśli chcesz utworzyć nową instancję z typem protokołu, musisz dodać init() do deklaracji protokół:

protocol SomeProtocol: ConformsToOtherProtocolIfNeeded { 
    init(...) { ... } 
    func someFunction(...) { ... } 
} 

class Class1: SomeProtocol { 
    init(...) { ... } 
    func someFunction(...) { ... } 
} 

class Class2: SomeProtocol { 
    init(...) { ... } 
    func someFunction(...) { ... } 
} 

stwierdzenie array (jak wyżej):

var someProtocols: Array<SomeProtocol.Type> = [ 
    Class1.self, 
    Class2.self, 
] 

i jeśli chcesz używać someFunction, trzeba utworzyć instancję, ponieważ elementy w tablicy nie instancja. Przykład:

for sp in someProtocols { 
    let instance = sp.init() 
    instance.someFunction() 
} 

Jeśli chcesz porównać typ klasy, trzeba także utworzyć instancję. (Nie można więc używać elementu tablicy bezpośrednio (sp)).

Przykłady:

if type(of: instance) == type(of: otherInstance) { ... } 
if instance is SomeProtocol { ... } 
if instance is Class1 { ... } 
Powiązane problemy