Qual è la differenza tra ereditarietà e categorie nell’Obiettivo-C

Qualcuno può spiegarmi la differenza tra le categorie e l’ereditarietà nell’Obiettivo C? Ho letto la voce su Wikipedia e la discussione sulle categorie non sembra diversa da quella dell’ereditarietà. Ho anche esaminato la discussione sull’argomento nel libro “Open iPhone Development” e ancora non capisco.

A volte, l’ereditarietà sembra più un problema che non ne vale la pena. È correttamente utilizzato quando si desidera aggiungere qualcosa a una class esistente che è un cambiamento nel comportamento di quella class.

Con una categoria, vuoi solo che l’object esistente faccia un po ‘di più. Come già indicato, se si desidera solo avere una class stringa che gestisce la compressione, non è necessario creare una sottoclass della class stringa, basta creare una categoria che gestisca la compressione. In questo modo, non è necessario modificare il tipo di classi stringa già utilizzate.

L’indizio è nella limitazione secondo cui le categorie aggiungono solo metodi, non è ansible aggiungere variabili a una class utilizzando le categorie. Se la class ha bisogno di più proprietà, allora deve essere sottoclassata. (Modifica: è ansible utilizzare la memorizzazione associativa, credo).

Le categorie sono un buon modo per aggiungere funzionalità e allo stesso tempo conformarsi a un principio orientato agli oggetti per preferire la composizione all’ereditarietà.

Modifica gennaio 2012

Le cose sono cambiate ora. Con il compilatore LLVM corrente e il runtime moderno a 64 bit, è ansible aggiungere iVars e proprietà alle estensioni di class (non alle categorie). Ciò ti consente di mantenere iVar privati ​​fuori dall’interfaccia pubblica. Ma se dichiari proprietà per iVar, possono ancora essere accedute / modificate tramite KVC, perché non esiste ancora un metodo privato in Objective-C.

Le categorie consentono di aggiungere metodi a classi esistenti. Quindi, piuttosto che sottoclass NSData per aggiungere i tuoi funky nuovi metodi di crittografia, puoi aggiungerli direttamente alla class NSData. Ogni object NSData nella tua app ora ha accesso a questi metodi.

Per vedere quanto può essere utile, guarda: CocoaDev

Una delle illustrazioni preferite delle categorie Objective-c in azione è NSString. NSString è definito nel framework Foundation, che non ha nozioni di viste o windows. Tuttavia, se si utilizza una NSString in un’applicazione Cocoa, si noterà che risponde a messaggi come – drawInRect:withAttributes:

AppKit definisce una categoria per NSString che fornisce metodi di disegno aggiuntivi. La categoria consente l’aggiunta di nuovi metodi a una class esistente, quindi ci occupiamo ancora solo di NSStrings. Se AppKit invece implementasse il disegno per sottoclass dovremmo occuparci di “AppKitStrings” o “NSSDrawableStrings” o qualcosa del genere.

Le categorie consentono di aggiungere applicazioni o metodi specifici del dominio alle classi esistenti. Può essere abbastanza potente e conveniente.

Se a un programmatore viene fornito un set completo di codice sorgente per una libreria o un’applicazione di codice, puoi diventare pazzo e cambiare tutto ciò che è necessario per raggiungere il tuo objective di programmazione con quel codice.

Sfortunatamente, questo non è sempre il caso o addirittura auspicabile. Un sacco di volte ti viene data una libreria binaria / kit di oggetti e un set di intestazioni con cui accontentarti.

Quindi è necessaria una nuova funzionalità per una class in modo da poter fare un paio di cose:

  1. crea una nuova class intera invece di una class azionaria – replicando tutte le sue funzioni e membri quindi riscrivi tutto il codice per utilizzare la nuova class.

  2. creare una nuova class wrapper che contenga la class stock come membro (compositing) e riscrivere la codebase per utilizzare la nuova class.

  3. patch binari della libreria per cambiare il codice (buona fortuna)

  4. costringere il compilatore a vedere la tua nuova class come quella vecchia e sperare che non dipenda da una certa dimensione o luogo in memoria e specifici punti di ingresso.

  5. specializzazione sottoclass – creare sottoclassi per aggiungere funzionalità e modificare il codice del driver per utilizzare invece la sottoclass – in teoria dovrebbero esserci pochi problemi e se è necessario aggiungere membri dati è necessario, ma l’impronta della memoria sarà diversa. Hai il vantaggio di avere sia il nuovo codice che il vecchio codice disponibili nella sottoclass e scegliere quale usare, il metodo della class base o il metodo sovrascritto.

  6. modificare la class objc necessaria con una definizione di categoria contenente metodi per eseguire ciò che si desidera e / o sovrascrivere i vecchi metodi nelle classi di magazzino.

    Questo può anche correggere gli errori nella libreria o personalizzare i metodi per i nuovi dispositivi hardware o altro. Non è una panacea, ma consente l’aggiunta del metodo di class senza ricompilare la class / libreria che è rimasta invariata. La class originale è la stessa nel codice, nella dimensione della memoria e nei punti di ingresso, quindi le app legacy non si interrompono. Il compilatore inserisce semplicemente i nuovi metodi nel runtime come appartenenti a quella class e sovrascrive i metodi con la stessa firma del codice originale.

    un esempio:

    Hai una class Bing che viene trasmessa a un terminale, ma non a una porta seriale, e ora è quello di cui hai bisogno. (per qualche ragione). Hai Bing.h e libBing.so, ma non Bing.m nel tuo kit.

    La class Bing fa tutti i tipi di cose internamente, non sai nemmeno cosa, hai solo la API pubblica nell’intestazione.

    Sei intelligente, quindi crei una categoria (SerialOutput) per la class Bing.

     [Bing_SerialOutput.m] @interface Bing (SerialOutput) // a category - (void)ToSerial: (SerialPort*) port ; @end @implementation Bing (SerialOutput) - (void)ToSerial: (SerialPort*) port { ... /// serial output code /// } @end 

    Il compilatore obbliga a creare un object che possa essere collegato alla tua app e il runtime ora sa che Bing risponde a @selector (ToSerial 🙂 e puoi usarlo come se la class Bing fosse stata costruita con quel metodo. Non è ansible aggiungere solo metodi ai membri dei dati e questo non era inteso a creare tumori giganteschi di codice associati alle classi di base, ma ha i suoi vantaggi rispetto alle lingue tipizzate rigorosamente.

Penso che alcune di queste risposte indichino almeno l’idea che l’ereditarietà sia un modo più pesante di aggiungere funzionalità a una class esistente, mentre le categorie sono più leggere.

L’ereditarietà viene utilizzata quando si crea una nuova gerarchia di classi (tutte le campane e i fischi) e si può ragionevolmente aggiungere un sacco di lavoro quando viene scelto come metodo per aggiungere funzionalità alle classi esistenti.

Come lo ha detto qualcun altro … Se si utilizza l’ereditarietà per aggiungere un nuovo metodo ad esempio a NSString, è necessario andare e modificare il tipo che si sta utilizzando in qualsiasi altro codice in cui si desidera utilizzare questo nuovo metodo. Se, tuttavia, si utilizzano le categorie, è ansible chiamare semplicemente il metodo su tipi NSString esistenti, senza sottoclassi.

Gli stessi fini possono essere raggiunti con entrambi, ma le categorie sembrano darci un’opzione che è più semplice e richiede meno manutenzione (probabilmente).

Qualcuno sa se ci sono situazioni in cui le categorie sono assolutamente necessarie?

Una categoria è come un mixin: un modulo in Ruby o un po ‘come un’interfaccia in Java. Puoi pensarlo come “metodi nudi”. Quando aggiungi una categoria, stai aggiungendo metodi alla class. L’articolo di Wikipedia ha buone cose .

Il modo migliore per guardare a questa differenza è che: 1. ereditarietà: quando vuoi girarla esattamente sulla tua strada. esempio: AsyncImageView per implementare il caricamento lazy. Che viene fatto ereditando UIView. 2. categoria: voglio solo aggiungere un sapore in più ad esso. esempio: vogliamo sostituire tutti gli spazi dal testo di un campo di testo

  @interface UITextField(setText) - (NSString *)replaceEscape; @end @implementation UITextField(setText) - (NSString *)replaceEscape { self.text=[self.text stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]]; return self.text; } @end 

— Aggiungerà una nuova proprietà al campo di testo affinché tu possa sfuggire a tutti gli spazi bianchi. Proprio come aggiungere una nuova dimensione ad esso senza cambiare completamente la sua strada.