2012-01-12 6 views
20

C'è qualche motivo per dichiarare un ivar privato in @interface anziché @implementation?Ivar privato in @interface o @implementation

vedo codice come questo in tutto l'Internet (inclusa la documentazione fornita da Apple):

foo.h

@interface Foo : NSObject { 
@private 
    id _foo; 
} 
@end 

Foo.m

@implementation Foo 
// do something with _foo 
@end 

Il Il file header definisce l'interfaccia pubblica di una classe, mentre un ivar privato è ... bene ... privato. Allora perché non dichiararlo così?

foo.h

@interface Foo : NSObject 
@end 

Foo.m

@implementation Foo { 
@private 
    id _foo; 
} 

// do something with _foo 
@end 

risposta

24

Dichiarazione variabili di istanza in @implementation è una caratteristica recente di Obj-C, questo è il motivo per cui si vede un sacco di codice con loro nel @interface - non c'era altra scelta.

Se si utilizza un compilatore che supporta la dichiarazione delle variabili di istanza nell'implementazione che dichiara che è probabile che sia il migliore predefinito, inserirle nell'interfaccia solo se è necessario l'accesso da parte di altri.

Modifica: Altre Informazioni

variabili di istanza dichiarate nella realizzazione sono implicitamente nascosto (in modo efficace privata) e la visibilità non può essere cambiato - @public, @protected e @private non producono errori di compilazione (con l'attuale Clang almeno) ma vengono ignorati.

+1

In particolare, il compilatore in questione sembra essere Clang> 2. (Esistente) GCC non lo farà. –

+0

In questo contesto, 'Clang' è uguale a 'LLVM', giusto? –

+0

@ranReloaded - no. Vi sono gcc - gcc front e backend, gcc-llvm - gcc frontend, llvm backend - e clang - clang frontend, llvm backend. Ho provato solo su clang, Josh è stato testato su uno dei gcc's. YMMV, provalo con qualunque compilatore stai usando e vedi. – CRD

4

Preferisci lo @interface se hai bisogno del supporto del compilatore per il targeting di vecchi sistemi o versioni di Xcode.

Se sei sicuro di non aver bisogno di quella retrocompatibilità, direi che è meglio inserirlo nello @implementation.

  • Penso che @private sia un buon valore predefinito.
  • Riduce al minimo i tempi di compilazione e riduce le dipendenze se lo si utilizza correttamente.
  • È possibile ridurre gran parte di tale rumore nella parte superiore dell'intestazione. Molte persone metteranno #imports per i loro ivar, ma dovrebbero usare una dichiarazione forward come default. Quindi puoi rimuovere molte #import e molte dichiarazioni in avanti dalla tua intestazione.
3

Le direttive @public, @protected e @private sono non vincolante obiettivo-C, sono compilatore suggerimenti in merito l'accessibilità delle variabili. NON VI LIMITA a impedirne l'accesso.

esempio:

@interface Example : Object 
{ 
@public 
int x; 
@private 
int y; 
} 
... 


... 
id ex = [[Example alloc ] init]; 
ex->x = 10; 
ex->y = -10; 
printf(" x = %d , y = %d \n", ex->x , ex->y); 
... 

Il compilatore gcc sputa:

main.m: 56: 1: avviso: istanza variabile ‘y’ è @private; questo sarà un errore grave in futuro

Main.m: 57: 1: warning: la variabile di istanza 'y' è @private; questo sarà un errore hardware in futuro

una volta per ogni accesso "innapropriate" per membro y "privato", ma compila comunque.

Quando viene eseguito si ottiene

x = 10 , y = -10 

Così è davvero a te non scrivere codice di accesso in questo modo, ma perché objc è un superset di C, sintassi C funziona bene, e tutti le classi sono trasparenti.

È possibile impostare il compilatore per trattare questi avvisi come errori e cauzione, ma l'obiettivo C non è impostato internamente per questo tipo di rigore. Il dispatch del metodo dinamico dovrebbe verificare la portata e l'autorizzazione per ogni chiamata (slooooowwwww ...), quindi al di là di un avviso in fase di compilazione, il sistema si aspetta che il programmatore rispetti l'ambito dei membri dei dati.

Ci sono diversi trucchi per ottenere la privacy dei membri in oggettivo-C. Uno è quello di assicurarsi di inserire l'interfaccia e le implementazioni della classe in file separati .h e .m, rispettivamente, e di mettere i membri dei dati nel file di implementazione (il file .m). Quindi i file che importano le intestazioni non hanno accesso ai membri dati, solo la classe stessa. Quindi fornire i metodi di accesso (o non) nell'intestazione. È possibile implementare le funzioni setter/getter nel file di implementazione per scopi diagnostici se si desidera e saranno richiamabili, ma l'accesso diretto ai dati non sarà possibile.

esempio:

@implementation Example2 :Object 
{ 
//nothing here 
} 
double hidden_d; // hey now this isn't seen by other files. 
id classdata; // neither is this. 

-(id) classdata { return [classdata data]; } // public accessor 
-(void) method2 { ... } 
@end 

// this is an "informal category" with no @interface section 
// these methods are not "published" in the header but are valid for the class 

@implementation Example2 (private) 
-(void)set_hidden_d:(double)d { hidden_d = d; } 

// You can only return by reference, not value, and the runtime sees (id) outside this file. 
// You must cast to (double*) and de-reference it to use it outside of this file. 
-(id) hidden_d_ptr { return &hidden_d;} 
@end 

... 
[Main.m] 
... 
ex2 = [[Example2 alloc] init]; 

double d = ex2->hidden_d; // error: 'struct Example2’ has no member named ‘hidden_d’ 
id data = ex2->classdata; // error: 'struct Example2’ has no member named ‘classdata’ 
id data = [ex2 classdata] // OK 

[ex2 set_hidden_d : 6.28318 ]; // warning:'Example2' may not respond to '-set_hidden_d:' 

double* dp = [ex2 hidden_d_ptr]; // (SO UGLY) warning: initialization from incompatible pointer type 
           // use (double*)cast -- <pointer-to-pointer conversion> 
double d = (*dp); // dereference pointer (also UGLY). 

... 

Il compilatore emetterà avvisi per tali imbrogli palesi, ma andrà avanti e fiducia che si sa cosa si sta facendo, e che hai le tue ragioni (do (davvero?) tu?). Sembra un sacco di lavoro? Incline a errori? Yay Baby! Prova a riconsiderare il tuo codice prima di ricorrere ai trucchi magici e alla polpetta come questo.

Ma eccolo. In bocca al lupo.

+0

starei lontano da 'double' su iOS;) –