2016-01-25 9 views
8

API Objective-C fanno un sacco di costruzione oggetto utilizzando metodi di classe:Swift errore: metodo di classe "someMethod() non è disponibile, classe uso oggetto costruzione()"

+ (NSDate *)date; 
+ (NSURL *)urlWithString:(NSString *)string; 
+ (instancetype)layerWithSession:(AVCaptureSession *)session 

Qualche volta ho persino vedere questi che appaiono nelle vecchi tutorial Swift come metodi di classe, ma quando provo a chiamarli, ottengo errori del compilatore come questi:

date() is unavailable, use object construction NSDate()

urlWithString() is unavailable, use object construction NSURL(string:)

layerWithSession() is unavailable, use object construction AVCaptureVideoPreviewLayer(session:)

(vedo dichiarazioni come convenience init(URL url: NSURL) nella documentazione e nelle interfacce Swift generati di Xcode per le classi SDK, ma cosa hanno a che fare con tutto questo?)

Questo accade anche quando dichiaro metodi di classe nel mio codice objC, come per un pattern Singleton:

@interface MyManager: NSObject 
+ (instancetype)manager; 
@end 

Quando cerco di usarle da Swift, ottengo lo stesso tipo di errori:

let manager = MyManager.manager() 
// error: 'manager()' is unavailable, use object construction 'MyManager()' 

Cosa dà? Come posso correggere questi errori?

+0

Vedo un sacco di domande con questo problema come causa principale, ma dove la discussione e le risposte sono troppo legate a casi specifici da contrassegnare come duplicati l'uno dell'altro. Ecco un tentativo di generalizzare il problema: per gli altri che hanno il potere di chiudere i duplicati, sentitevi liberi di ingannare questo D & R se pensate che risolva il problema abbastanza bene. (Mi asterrò da me stesso per lasciare che la comunità sia il giudice della destrezza.) – rickster

risposta

13

Swift elimina il modello di inizializzazione alloc/init da Objective-C e lo sostituisce con una sintassi di costruzione simile a quella vista in Java o C++.

Quando, in un'interfaccia di classe Swift (o nella documentazione di Apple), si vede qualcosa di simile:

class Thing { 
    init(number: Int) 
} 

Questo significa che è possibile creare un Thing chiamando con il seguente "sintassi di costruzione":

let thingTwo = Thing(number: 2) 

non importa se si tratta di un class o un struct, o se vedete altre cose come convenience o required o public prima init ... è così che lo usi. (Naturalmente, sostituire Thing con il tipo che si sta utilizzando, number con qualsiasi etichetta del parametro (s) nella inizializzazione reale che si desidera, e 2 con un valore appropriato per tale parametro.)


Quando classi originariamente definiti in ObjC sono importati in Swift, il compilatore Swift fa un sacco di cose per far sembrare quelle API più simili al codice Swift nativo. Una di queste cose è sostituire il modello comune di utilizzare i metodi di classe come costruttori di convenienza con inizializzatori reali.

Tutti gli esempi dalla domanda:

+ (NSDate *)date; 
+ (NSURL *)urlWithString:(NSString *)string; 
+ (instancetype)layerWithSession:(AVCaptureSession *)session; 

... sono tali costruttori convenienza. Ciò significa che importare in Swift non come metodi di classe (NSDate.date(), NSURL.urlWithString(), ecc), ma come inizializzatori:

class NSDate { 
    init() 
} 
class NSURL { 
    init?(string: String) 
} 
class AVCaptureVideoPreviewLayer { 
    init!(session: AVCaptureSession) 
} 

...e li chiami con la sintassi oggetto costruzione:

let date = NSDate() 
let url = NSURL(string: "http://apple.com") 
let layer = AVCaptureVideoPreviewLayer(session: mySession) 

In realtà, quando si vede i messaggi di errore di "non è disponibile, l'uso di costruzione oggetto", Xcode offre quasi sempre un automatico fix-it per inserire la sintassi corretta.


Come funziona? Swift cerca possibili "nomi di base" di una classe e trova corrispondenze tra questi nomi e il metodo di metodo/nome del metodo di inizializzazione. (Controlla anche per metodi di classe che restituiscono un'istanza di quella classe, inizializzatori che iniziano con "init ...", ecc)

Per esempio, Swift vede quanto segue come un costruttore di convenienza:

@interface PDQMediaManager: NSObject 
+ (PDQMediaManager *)manager; 
@end 

Questo può essere un problema se quello che tu (o lo sviluppatore originale di) intendi è che quella classe agisca come un singleton e il metodo +manager per restituire l'istanza singleton - se uno sviluppatore Swift scrive PDQMediaManager(), saranno costruendo una nuova istanza invece di recuperare il singleton.

In questo caso, una buona soluzione è quella di rinominare il metodo di Singleton:

+ (PDQMediaManager *)defaultManager; 

Questo non verrà visto come un costruttore di convenienza, i clienti in modo da Swift possono chiamare PDQMediaManager.defaultManager() per ottenere l'istanza Singleton.

È inoltre possibile utilizzare il NS_SWIFT_NAME per rinominare il metodo Singleton solo per i clienti Swift:

+ (PDQMediaManager *)manager NS_SWIFT_NAME(defaultManager()); 

Se non si controlla la base di codice con il metodo incriminato, la cosa migliore è probabilmente quello di scrivere il proprio objC codice per inoltrare al metodo singleton ed esporlo al codice Swift tramite un'intestazione di bridging.

Problemi correlati