Classe base nativa Swift o NSObject


105

Ho provato alcuni isa swizzling con Swift e ho scoperto che funziona solo quando NSObject è una superclasse (direttamente o più in alto) o utilizzando la decorazione "@objc". Altrimenti seguirà uno stile di invio statico e vtable, come C ++.

È normale definire una classe Swift senza una classe base Cocoa / NSObject? Se sono preoccupato, questo significa rinunciare a gran parte del dinamismo di Objective-C, come l'intercettazione del metodo e l'introspezione in fase di esecuzione.

Il comportamento dinamico in fase di esecuzione è al centro di funzionalità come osservatori di proprietà, dati di base, programmazione orientata agli aspetti , messaggistica di ordine superiore , framework di analisi e registrazione e così via.

L'uso dello stile di invocazione del metodo di Objective-C aggiunge circa 20 operandi del codice macchina a una chiamata di metodo, quindi in determinate situazioni ( molte chiamate strette a metodi con corpi piccoli ) l'invio statico e vtable in stile C ++ può funzionare meglio.

Ma data la regola generale 95-5 (il 95% dei guadagni in termini di prestazioni proviene dalla messa a punto del 5% del codice ), non ha senso iniziare con le potenti funzionalità dinamiche e rafforzare dove necessario?


Correlati: Swift supporta la programmazione orientata agli aspetti? stackoverflow.com/a/24137487/404201
Jasper Blues

Risposte:


109

Classi Swift che sono sottoclassi di NSObject:

  • sono classi Objective-C stesse
  • utilizzare objc_msgSend()per le chiamate alla (maggior parte dei) metodi
  • fornire metadati di runtime Objective-C per (la maggior parte delle) implementazioni dei metodi

Classi Swift che non sono sottoclassi di NSObject:

  • sono classi Objective-C, ma implementano solo una manciata di metodi per la compatibilità con NSObject
  • non utilizzare objc_msgSend()per le chiamate ai loro metodi (per impostazione predefinita)
  • non fornire metadati di runtime Objective-C per le loro implementazioni di metodo (per impostazione predefinita)

La creazione di sottoclassi di NSObject in Swift offre flessibilità di runtime Objective-C ma anche prestazioni Objective-C. Evitare NSObject può migliorare le prestazioni se non è necessaria la flessibilità di Objective-C.

Modificare:

Con Xcode 6 beta 6, viene visualizzato l'attributo dinamico. Questo ci consente di indicare a Swift che un metodo dovrebbe utilizzare l'invio dinamico e quindi supporterà l'intercettazione.

public dynamic func foobar() -> AnyObject {
}

1
Che tipo di invio utilizza Swift al posto di objc_msgSend? È statico?
Bill

12
Swift può utilizzare objc_msgSend invio, invio di tabelle virtuali, invio diretto o inlining.
Greg Parker

statico: meno di 1,1 ns. vtable 1.1ns, msgInvia 4.9ns. . (Dipende dall'hardware ovviamente). . Sembra che Swift sia un ottimo linguaggio per la programmazione a livello di sistema, ma per le app sarei riluttante a rinunciare alle funzionalità dinamiche, anche se sarei felice se passassero al compilatore come in Garbage Collection vs ARC. . . Ho sentito dire che l'invio statico consente una migliore previsione dei rami (e quindi prestazioni) su sistemi multi-core. Vero?
Jasper Blues

"Le classi Swift che non sono sottoclassi di NSObject sono classi Objective-C" - puoi fornire un collegamento a dove hai trovato quello dichiarato?
Matt S.

1
Quindi, in sintesi, dovrei sottoclassare NSObject in Swift solo se ho bisogno di comunicare con il codice Objective C?
MobileMon

14

Ho anche scoperto che se basavo una classe Swift su NSObject, vedevo un comportamento inaspettato in fase di esecuzione che poteva nascondere bug di codifica. Ecco un esempio.

In questo esempio, dove non ci basiamo su NSObject, il compilatore individua correttamente l'errore in testIncorrect_CompilerShouldSpot, riportando "... 'MyClass' non è convertibile in 'MirrorDisposition'"

class MyClass {
  let mString = "Test"

  func getAsString() -> String {
    return mString
  }

  func testIncorrect_CompilerShouldSpot() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject == myString) {
        // Do something
      }
  }

  func testCorrect_CorrectlyWritten() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject.getAsString() == myString) {
        // Do something
      }
  }
}

In questo esempio, dove ci basiamo su NSObject , il compilatore non individua l'errore in testIncorrect_CompilerShouldSpot:

class myClass : NSObject {
  let mString = "Test"

  func getAsString() -> String {
    return mString
  }

  func testIncorrect_CompilerShouldSpot() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject == myString) {
        // Do something
      }
  }

  func testCorrect_CorrectlyWritten() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject.getAsString() == myString) {
        // Do something
      }
  }
}

Immagino che la morale sia, l'unica base su NSObject dove devi davvero!


1
Grazie per le informazioni.
Jasper Blues

13

In base al linguaggio di riferimento, non è necessario che le classi sottoclasse alcuna classe radice standard, quindi è possibile includere o omettere una superclasse secondo necessità.

Si noti che l'omissione di una superclasse dalla dichiarazione della classe, non assegna una superclasse di base implicita di alcun tipo. Definisce una classe base, che diventerà effettivamente la radice di una gerarchia di classi indipendente.

Dalla lingua di riferimento:

Le classi Swift non ereditano da una classe base universale. Le classi definite senza specificare una superclasse diventano automaticamente classi base su cui costruire.

Il tentativo di fare riferimento superda una classe senza una super classe (cioè una classe base) risulterà in un errore in fase di compilazione

'super' members cannot be referenced in a root class

1
Si. Se provi a creare un file sorgente Cocoa o Cocoa Touch nel contesto di un progetto, il modulo in cui inserisci la sottoclasse ti impedisce di creare la classe lasciandola vuota. È possibile rimuovere la superclasse in un secondo momento, una volta creata.
Cezar

1
Grazie. L'uso dell'invio statico e vtable ha senso per la programmazione dei sistemi o l'ottimizzazione delle prestazioni. Per coerenza con Cocoa nativo, penso che dinamico dovrebbe essere l'impostazione predefinita. (A meno che qualcuno non possa convincermi del contrario, da qui questa domanda).
Jasper Blues

1

Credo che la stragrande maggioranza dei dati Swift non lo sarà objc. Solo le parti che devono comunicare con l'infrastruttura Objective C saranno esplicitamente contrassegnate come tali.

Fino a che punto l'introspezione in fase di esecuzione verrà aggiunta alla lingua, non lo so. L'intercettazione del metodo sarà probabilmente possibile solo se il metodo lo consente esplicitamente. Questa è la mia ipotesi, ma solo i progettisti di linguaggi all'interno di Apple sanno davvero dove stanno andando davvero.


Per me ha senso impostare il dinamismo come predefinito (estendendo NSObject, usando la decorazione '@objc' o qualche altro approccio). Sembra che quando non esiste una classe base, Swift favorirà l'invio statico / vtable, che offre prestazioni migliori, ma nella maggior parte dei casi ciò non sarà necessario. . (Il 90% dei guadagni deriva dall'ottimizzazione del 10% del codice). La mia speranza è che la coerenza con il dinamismo di Objective-C sia opt-out piuttosto che opt-in.
Jasper Blues

Il modo in cui la lingua è progettata, è un opt-in opzionale. Per quanto ne sappiamo, potrebbe benissimo essere che in futuro verranno aggiunte API di sistema che non sono nativamente oggettive. A medio / lungo termine possono anche migrare le librerie esistenti in codice non objc con un sottile strato di compatibilità in objc che chiama nel codice non objc. Semplicemente non lo sappiamo. Probabilmente saremo in grado di farlo educated guessestra qualche anno. Ma per il momento è solo un grande punto interrogativo.
File analogico

@ JasperBlues Direi che il dinamismo non è necessario per la maggior parte del tempo e preferirei avere le prestazioni di default e quindi optare per il comportamento dinamico. A ciascuno il suo credo :)
Lance

@Lance Hmmmm. Può essere. Sono ancora preoccupato per: osservatori, AOP, framework di test mocking, framework analitici, ecc. e il problema delle prestazioni è che il 90% dei guadagni proviene dall'ottimizzazione del 10%. . quindi questa è la mia logica: opt-in per quei casi del 10%. Penso che AOP sarà un grosso problema per le app aziendali iOS, ma potrebbe essere fatto utilizzando uno strumento di compilazione come in C ++. . certamente giochi, informatica scientifica, grafica, ecc., gli sviluppatori apprezzeranno maggiori prestazioni :)
Jasper Blues

1

Quanto segue viene copiato dallo Swift-eBook di Apple e fornisce una risposta appropriata alla tua domanda:

Definizione di una classe base

Qualsiasi classe che non eredita da un'altra classe è nota come classe base.

Le classi Swift non ereditano da una classe base universale. Le classi definite senza specificare una superclasse diventano automaticamente classi base su cui costruire.


Riferimento

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_251


-6

È normale. Guarda gli obiettivi di progettazione di Swift: l'obiettivo è far sparire enormi classi di problemi di programmazione. Il metodo swizzling probabilmente non è una delle cose che vuoi fare con Swift.


3
Il metodo swizzling è un modo per ottenere il modello di intercettazione in fase di esecuzione. Uno degli usi per questo è applicare preoccupazioni trasversali secondo i principi della programmazione orientata agli aspetti. Le notifiche di Apple, gli osservatori di proprietà (integrati in swift), i dati principali utilizzano tutti lo swizzling con buoni risultati. . Una delle cose che ha innamorato le persone di Objective-C nonostante la sua sintassi goffa era questo dinamismo, che rende facile fornire soluzioni eleganti ogni volta che è richiesto il modello di intercettazione. . infatti non esisteva un quadro AOP formale per Objc, perché le materie prime erano così buone.
Jasper Blues

Il fatto che sia così che viene fatto ora non significa che sia così che sarà fatto domani. Come dici tu, le notifiche, gli osservatori della proprietà, i delegati e simili sono o possono essere forniti in modo nativo. La lingua si evolverà e non sappiamo in quale direzione. Ha guadagnato molto in termini di supporto per la programmazione funzionale. Ma lo fa in un modo insolito, quindi non ho idea di quanto manchi. Tuttavia, per quanto ne sappiamo, potrebbero essere diretti a scoraggiare la mutazione e incoraggiare la pura programmazione funzionale. E se il futuro dell'interfaccia utente fosse reattivo? Nessun modo per saperlo ancora.
File analogico

1
Sì, ma tutte queste cose dipendono dall'intercettazione, che può essere eseguita in due modi: in fase di compilazione (C ++) o runtime (Ruby, Objective-C, Java (tramite asm, cglib, ApsectJ, ecc.)). I linguaggi moderni tendono a farlo in fase di esecuzione. La gente amava Objective-C, perché si comportava come un linguaggio moderno nonostante fosse un vecchio codger. Come dici tu, più di questo viene inserito nella lingua, meglio è (a condizione che sia fatto bene!). . ma per ora possiamo agganciarci all'eredità fornita da Objc / Foundation, motivo per cui spero che l'estensione di NSObject diventi una pratica standard per le app di tutti i giorni nei prossimi anni.
Jasper Blues
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.