Restituisci instancetype in Swift

Sto provando a fare questa estensione:

extension UIViewController { class func initialize(storyboardName: String, storyboardId: String) -> Self { let storyboad = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! Self return controller } } 

Ma ottengo l’errore di compilazione:

errore: imansible convertire l’espressione di ritorno di tipo ‘UIViewController’ per restituire il tipo ‘Self’

È ansible? Inoltre voglio renderlo come init(storyboardName: String, storyboardId: String)

Simile all’utilizzo di ‘self’ nelle funzioni di estensione della class in Swift , è ansible definire un metodo helper generico che inferisce il tipo di sé dal contesto chiamante:

 extension UIViewController { class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self { return instantiateFromStoryboardHelper(storyboardName, storyboardId: storyboardId) } private class func instantiateFromStoryboardHelper(storyboardName: String, storyboardId: String) -> T { let storyboard = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboard.instantiateViewControllerWithIdentifier(storyboardId) as! T return controller } } 

Poi

 let vc = MyViewController.instantiateFromStoryboard("name", storyboardId: "id") 

compila e il tipo viene dedotto come MyViewController .


Aggiornamento per Swift 3:

 extension UIViewController { class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self { return instantiateFromStoryboardHelper(storyboardName: storyboardName, storyboardId: storyboardId) } private class func instantiateFromStoryboardHelper(storyboardName: String, storyboardId: String) -> T { let storyboard = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboard.instantiateViewController(withIdentifier: storyboardId) as! T return controller } } 

Un’altra ansible soluzione, utilizzando unsafeDowncast :

 extension UIViewController { class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self { let storyboard = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboard.instantiateViewController(withIdentifier: storyboardId) return unsafeDowncast(controller, to: self) } } 

Self è determinato in fase di compilazione, non in runtime. Nel tuo codice, Self è esattamente equivalente a UIViewController , non “la sottoclass che capita di chiamarla”. Ciò restituirà UIViewController e il chiamante dovrà inserirsi nella sottoclass giusta. Immagino sia quello che stavi cercando di evitare (anche se è il modo “normale Cocoa” per farlo, quindi solo restituire UIViewController è probabilmente la soluzione migliore).

Nota: non si deve nominare la funzione initialize in alcun caso. Questa è una funzione di class esistente di NSObject e causerebbe al massimo confusione, bug nel peggiore dei casi.

Ma se si desidera evitare la chiamata del chiamante, la sottoclass di solito non è lo strumento per aggiungere funzionalità in Swift. Invece, di solito vuoi generici e protocolli. In questo caso, i generici sono tutto ciò di cui hai bisogno.

 func instantiateViewController(storyboardName: String, storyboardId: String) -> VC { let storyboad = UIStoryboard(name name: storyboardName, bundle: nil) let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! VC return controller } 

Questo non è un metodo di class. È solo una funzione Non c’è bisogno di una lezione qui.

 let tvc: UITableViewController = instantiateViewController(name: name, storyboardId: storyboardId) 

Un altro modo è usare un protocollo, che ti permette anche di tornare al Self .

 protocol StoryboardGeneratable { } extension UIViewController: StoryboardGeneratable { } extension StoryboardGeneratable where Self: UIViewController { static func initialize(storyboardName: String, storyboardId: String) -> Self { let storyboad = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboad.instantiateViewController(withIdentifier: storyboardId) as! Self return controller } }