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.
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