2016-03-21 3 views
1

Ich habe eine Reihe von AnyObject, und ich möchte es gegossen (oder einem seiner Objekte) auf einen generischen Typ T die gegebenen darf kein Array sein.cast [ANYOBJECT] zu gattungsgemäßen Art, der nicht eine Sammlung Typ sein können oder auch

Mein erster Versuch:

class MyClass<T> { 

    func someMethod() -> T? { 

     let anyObjectArray: [AnyObject] = // ... array obtained from Objective-C framework 

     if let objectsAsCollection = anyObjectArray as? T { // Cast always fails 
      return objectsAsCollection 
     } else if let firstObject = anyObjectArray.first as? T { 
      return firstObject 
     } else { 
      return nil 
     } 
    } 
} 

Der erste if let Guss schlägt fehl, wenn T ist ein Array (z T.self == Array<SomeObject>.self).

Ich würde so etwas wie dies versuchen, aber es geht irgendwie eine andere generische Parameter einzuführen, so nicht gültig ist, wie geschrieben:

extension MyClass where T == Array<U> { 

    fun someMethod() -> T? { 

     let anyObjectArray: [AnyObject] = // ... array obtained from Objective-C framework 

     if let objectsAsCollection = anyObjectArray.flatMap({ $0 as? U }) { 
      return objectsAsCollection 
     } else if let firstObject = anyObjectArray.first as? T { 
      return firstObject 
     } else { 
      return nil 
     } 
    } 
} 

Der Grund für den Wunsch, dies zu tun, ist, dass ich verwende ein Objective-C-Framework (RestKit), um Ergebnisse von einer REST-API zu erhalten. Ich baue eine Schicht auf der Oberseite in Swift, die diese Ergebnisse zurück, als eine bestimmte Art leitet die Anfrage nach, die gemacht wurde, so habe ich eine Result Typ, der etwa wie folgt aussieht:

enum Result<T>: { 
    case Success(T) 
    case Failure(Error) 
} 

Manchmal T ist ein Single Objekt, manchmal ist es ein Array von Objekten, aber das Objective-C-Framework gibt Ergebnisse immer als Array zurück.

+0

diskutiert wird. Dies wird schwierig ohne eine Einschränkung der Anzahl __verschiedener_ Objekttypen, die das Array "AnyObject" enthält. Zum Beispiel sagt Ihr '[AnyObject]' '' NSString' sowie eine 'NSNumber'-Instanzen: Wie werden diese in ein Array eines nativen Swift-Typs gegossen? Denken Sie daran, dass im Allgemeinen die Verwendung eines generischen, sagen wir "T", unterschiedlichen Typen erlaubt, die Methode aufzurufen, aber für jeden solchen Aufruf ist "T" auf einen einzigen Typ festgelegt. Wissen Sie sicher, dass das Array "AnyObject" immer nur einzelne Objekte desselben Typs enthält? – dfri

+0

@dfri Das Array 'AnyObject', das vom Objective-C-Framework zurückgegeben wird, kann theoretisch einen beliebigen Typ enthalten. In Wirklichkeit weiß ich jedoch, dass die REST-API ein Array eines bestimmten Typs zurückgibt, sodass für jeden Aufruf "T" entweder "SomeType" oder "Array " ist (wobei "SomeType" abhängig davon variiert, welcher Objekttyp zurückgegeben wird) vom Server). – Stuart

+0

Ich denke, dass ich kürzlich mit jemand anderem genau dasselbe Problem besprochen habe, und wir haben das gelöst, indem wir '[AnyObject]' durch 'NSArray' ersetzt haben. – Sulthan

Antwort

3

Die folgende Syntax wird funktionieren:

class MyClass<T> { 
    func someMethod() -> T? { 
     // let's not convert to [AnyObject] here, keep it as NSArray 
     let anyObjectArray: NSArray = ... 

     // For some reason conversion from [AnyObject] to [T] fails... 
     // However, conversion from NSArray is special 
     if let objectsAsCollection = anyObjectArray as? T { 
      return objectsAsCollection 
     } else if let firstObject = (anyObjectArray as [AnyObject]).first as? T { 
      return firstObject 
     } else { 
      return nil 
     } 
    } 
} 

Es ist aber für einen wahrscheinlichen Fehler nur eine Abhilfe. Wir können das Problem auf die folgende einkochen:

class GenericClass<T> { 
    func someMethod() { 
     let anyObjectArray: [AnyObject] = ["test1", "test2", "test3"] 

     print(T) //Array<String> 
     print(T.self == Array<String>.self) //true 

     print(anyObjectArray is [String]) //true 
     print(anyObjectArray is T) //false - BUG 
    } 
} 

let instance = GenericClass<[String]>() 
instance.someMethod() 

(berichtet: SR-1054)

, die mir wie ein klarer Fehler aussieht. Es sieht tatsächlich wie eine andere Instanz des Fehlers aus, der in Type of optionals cannot be inferred correctly in swift 2.2

+0

Oh, wie genial einfach, danke! Es ist nicht die erste Eigenart des Typecasting zwischen Objective-C und Swift, auf die ich gestoßen bin, aber ich habe nicht erwartet, dass die Lösung so einfach ist. Weißt du, warum die Besetzung von 'NSArray' anders behandelt wird? Ich vermute etwas, was mit der dynamischen Laufzeit von Objective-C zu tun hat, aber ich bin nicht sicher, wie das mit Swift-Generika interagiert ... – Stuart

+0

@Stuart Hmmm, ich dachte, ich wüsste den Grund, aber ich lag falsch. Dies sieht eher wie eine Problemumgehung aus. Ich werde versuchen, es noch ein wenig genauer zu untersuchen. Gerade heute haben wir einen Fehler beim Casting auf einen generischen Typ gemeldet, vielleicht ist das also auch ein Bug. – Sulthan

+1

@Stuart Ich denke, ich habe einen Beweis, dass es ein Fehler ist. – Sulthan

Verwandte Themen