2011-11-20 17 views
23

Ho avuto alcune discussioni relative all'uso di proprietà e variabili di istanza al lavoro, quindi mi piacerebbe trovare una risposta wiki per questo. Ora, so che non esiste un tipo di membro privato reale nell'obiettivo-c, tutto è praticamente pubblico. Tuttavia, sono un po 'preoccupato per il modo in cui dovremmo progettare le nostre classi e anche per rispettare i principi OOP. Mi piacerebbe sentire le opinioni di questi tre approcci progettuali:In che modo i membri pubblici e privati ​​devono essere implementati nell'obiettivo-c?

A. Secondo vari post e anche a un nuovo corso di sviluppo per iPhone della Stanford University, dovresti sempre usare le proprietà ovunque tu possa. Tuttavia, secondo me, questo approccio frena i principi di progettazione OOP perché in questo caso tutti i membri diventano pubblici. Perché devo pubblicare tutte le mie variabili di istanza interne/locali all'esterno? Inoltre, c'è un po 'poco (ma ancora) in testa se si utilizzano setter sintetizzati tramite proprietà, invece utilizzando direttamente ivar locale. Ecco un esempio:

//==== header file =====// 
@interface MyClass : NSObject 

@property (nonatomic, retain) NSString *publicMemberWithProperty; 
@property (nonatomic, retain) NSString *propertyForPrivateMember; 

@end 

B. Un altro approccio è quello di dichiarare Ivars in file di intestazione (senza dichiarare le proprietà relative) per i soci privati, e nello stesso file di intestazione, di dichiarare le proprietà puri (senza dichiarare Ivars relativi) per membri pubblici. In tal caso, gli ivar verrebbero usati direttamente nella classe. Questo approccio ha senso ma non utilizza tutti i vantaggi delle proprietà perché abbiamo manualmente il rilascio dei vecchi valori prima di impostarne di nuovi. Ecco un esempio:

//==== header file =====// 
@interface MyClass : NSObject{ 
    NSString *_privateMember; 
} 

@property (nonatomic, retain) NSString *publicMemberWithProperty; 

@end 

C. Dichiarare proprietà puri (senza dichiarare Ivars relativi) per i soci pubblici in file di intestazione, e di dichiararla proprietà puri (senza dichiarare ivars relativi) per i soci privati ​​a interfaccia privata nel file di implementazione . Questo approccio IMHO è più chiaro del primo, ma rimane la stessa domanda: perché dobbiamo avere proprietà per i membri interni/locali? Ecco un esempio: la libertà

//==== header file =====// 
@interface MyClass : NSObject 

@property (nonatomic, retain) NSString *publicMemberWithProperty; 

@end 

//==== implementation file =====// 
@interface MyClass() 

@property (nonatomic, retain) NSString *propertyForPrivateMember; 

@end 

Questa decisione mi infastidisce un po 'e mi piacerebbe trovare una conferma da rispettive fonti su come le cose dovrebbero essere fatte. Tuttavia, non sono stato in grado di trovare affermazioni così rigide nei documenti Apple, quindi per favore pubblica un link su Apple Docs, se esiste, o su qualsiasi altra teoria che lo chiarisca.

+0

Questa domanda sembra essere molto soggettiva ed è impossibile rispondere nel modo giusto. – Till

+0

La domanda è molto semplice - come dovrebbero essere implementati i membri privati ​​e pubblici in obiettivo-c, e dove potrei trovare la voce severa di Apple su questo. – Centurion

+1

Mi permetto di dissentire ma non approfondirò la discussione. Come suggerimento per dimostrare la mia richiesta, ti sei ricordato che ad es. i corsi di Stanford raccomandano l'uso di proprietà ovunque. – Till

risposta

21

Utilizzando le estensioni di classe è possibile avere proprietà private.

Una sintassi estensione classe è semplice:

All'interno del .m file, che ha la classe, creare una categoria senza nome:

.h

@interface OverlayViewController : UIViewController <VSClickWheelViewDelegate> 
- (IBAction)moreButtonClicked:(id)sender; 
- (IBAction)cancelButtonClicked:(id)sender; 
@end 

.m

#import "OverlayViewController.h" 

@interface OverlayViewController() 
@property(nonatomic) NSInteger amount; 
@property(retain,nonatomic)NSArray *colors; 
@end 

@implementation OverlayViewController 
@synthesize amount = amount_; 
@synthesize colors = colors_; 

//… 

@end 

Ora hai tutti gli aspetti delle proprietà per i membri privati, senza esporli al pubblico. Non ci dovrebbero essere overhead per le proprietà sintetizzate a getter/setter scritti, in quanto il compilatore creerà più o meno lo stesso in fase di compilazione.

Si noti che questo codice utilizza ivars sintetizzati. Non è necessaria alcuna dichiarazione di ivar nell'intestazione.

C'è un bel cocoawithlove article, su questo approccio.

Si chiede anche perché utilizzare le proprietà per ivars privati.Ci sono diversi buoni motivi:

  • proprietà prendersi cura della proprietà e gestione della memoria.
  • in qualsiasi momento, in futuro, puoi decidere di scrivere un getter/setter personalizzato. Ad esempio, per ricaricare una vista tabella, una volta che un ivar NSArray è stato appena impostato. Se hai usato le proprietà di conseguenza, non sono necessarie altre modifiche.
  • Proprietà supporto supporto codifica valori.
  • public readonly properties can be re-declared to private readwrite properties.

modifica
Poiché LLVM 3 è anche possibile, per dichiarare ivars nelle estensioni classe

@interface OverlayViewController(){ 
    NSInteger amount; 
    NSArray *colors; 
} 
@end 

o anche al blocco attuazione

@implementation OverlayViewController{ 
    NSInteger amount; 
    NSArray *colors; 
} 
//… 
@end 

vedi "WWDC2011: Sessione 322 - Obiettivi-C Advancemen ts in Depth "(~ 03: 00)

+0

Alcuni pedanti indicheranno : quelli non sono strettamente privati ​​perché i getter ei setter possono ancora essere usati da un altro codice se è disposto a sfidare un avvertimento del compilatore, ma per quanto mi riguarda un avviso del compilatore è sufficiente per avvisare gli altri. Questo è esattamente il modo (beh, dare o prendere i caratteri di trascinamento finali). Richiedo la dichiarazione delle variabili di istanza nel mio team: la regola che applico è che qualsiasi cosa nel file di intestazione è pubblica per definizione. – Tommy

+0

@Tommy: al mio lavoro, alcuni ragazzi hanno sostenuto, perché abbiamo bisogno di dichiarare e utilizzare le proprietà per i membri privati. Hanno detto che dovremmo usare le variabili di istanza pure e usarle direttamente nel codice di classe. La motivazione per farlo era che c'era un overhead di elaborazione quando si utilizza setter invece di rilasciare manualmente il vecchio valore e assegnare la nuova val direttamente a ivar. È solo curioso che ci siano dichiarazioni "consigliate" di Apple su questo? – Centurion

+0

@Centurion È possibile utilizzare proprietà "private" per [semplificare la gestione della memoria] (http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW4): "A volte può sembrare noioso o pedante, ma se si utilizzano metodi accessor coerenti, le probabilità di avere problemi con la gestione della memoria diminuiscono considerevolmente. codice, stai quasi certamente facendo la cosa sbagliata. " – albertamg

2

Analogamente al C++, l'obiettivo C fornisce ambiti pubblici, privati ​​e protetti. Fornisce inoltre un ambito del pacchetto simile all'ambito del pacchetto definito in Java. Le variabili pubbliche delle classi possono essere riferimenti ovunque nel programma. Le variabili private possono essere referenziate solo all'interno dei messaggi della classe che lo dichiara. Potrebbe essere utilizzato all'interno di messaggi che appartengono a QUALSIASI istanza della stessa classe. L'ambito del pacchetto è simile all'ambito pubblico all'interno della stessa immagine, ovvero eseguibile o libreria. Secondo la documentazione di Apple, su architetture a 64 bit, le variabili dell'ambito del pacchetto definite all'interno di un'immagine diversa devono essere considerate private. L'ambito della variabile è definito da @public, @private, @protected, modificatori @package. Questi modificatori possono essere utilizzati sia in modo simile a C++ o Java. Tutte le variabili elencate in una dichiarazione di ambito appartengono allo stesso ambito. Inoltre, le variabili possono essere elencate sulla stessa riga in cui viene dichiarato l'ambito.

@interface VariableScope : NSObject { 
     @public 
     int iVar0; 
     @protected 
     int iVar1; 
     @private 
     int iVar2; 
     @package 
     int iVar3; 

@public int iVar01, iVar02; 
@protected int iVar11, iVar12; 
@private int iVar21, iVar22; 
@package int iVar31, iVar32; 
} 
    @end 

Per maggiori informazioni cliccate sul link sottostante

http://cocoacast.com/?q=node/100

+4

Sì, ci sono parole chiave come pubbliche, private e protette, ma non limitano l'ambito durante il runtime. Ho eseguito alcuni test e posso dire che in obiettivo-c tutti i membri sono piuttosto pubblici. Puoi trovare il post sul mio esperimento qui: http://stackoverflow.com/questions/6122398/objective-c-why-private-ivars-are-not-hidden-from-the-outside-access-when-using – Centurion

3

In realtà non è un ambiente pulito, sicuro, a zero spese generali, soluzione a questo che è direttamente supportato dal linguaggio. Molte persone si accontentano delle attuali caratteristiche di visibilità, mentre molte persone sentono che mancano.

Il runtime potrebbe (ma non lo fa) fare questa distinzione con ivars e metodi. Il supporto di prima classe sarebbe il migliore, IMO. Fino ad allora, abbiamo alcuni idiomi astrazione:

Opzione A

è male - tutto è visibile. Non sono d'accordo sul fatto che sia un buon approccio e che non sia OOD (IMO).Se tutto è visibile, allora la classe debbono:

  • sostegno tutti i casi per come il cliente può utilizzare la classe (di solito irragionevole o indesiderabili)
  • o fornire loro un sacco di regole tramite la documentazione (doc gli aggiornamenti sono suscettibili di passare inosservato)
  • o le funzioni di accesso dovrebbero avere effetti collaterali (non OOD, e traduce spesso a 'non ignorare di accesso')

Opzione B

0.123.

presenta le carenze dell'opzione A e dell'opzione A, ai membri è possibile accedere tramite chiave.

Opzione C

Questo è leggermente più sicura. Come tutti gli altri, è comunque possibile utilizzare l'accesso con chiave e le sottoclassi possono ignorare gli accessor (anche se inconsapevolmente).

Opzione D

Un approccio a questo è quello di scrivere la classe come wrapper sopra su un tipo di implementazione. Puoi usare un tipo ObjC o un tipo C++ per questo. Si può favorire il C++ in cui la velocità è importante (è stata menzionata nell'OP).

Un approccio semplice per questo avrebbe preso una delle forme:

// inner ObjC type 
@class MONObjectImp; 

@interface MONObject : NSObject 
{ 
@private 
MONObjectImp * imp; 
} 
@end 


// Inner C++ type - Variant A 
class MONObjectImp { ... }; 

@interface MONObject : NSObject 
{ 
@private 
MONObjectImp imp; 
} 
@end 


// Inner C++ type - Variant B 
class MONObjectImp; 

@interface MONObject : NSObject 
{ 
@private 
MON::t_auto_pointer<MONObjectImp> imp; 
} 
@end 

(Nota:. Dal momento che questo è stato originariamente scritto, la possibilità di dichiarare Ivars nel blocco @implementation è stato introdotto Si dovrebbe dichiarare la vostra I tipi C++ sono lì se non è necessario supportare toolchain precedenti o il 'fragile' ABI OS X a 32 bit).

C++ Variant A non è "sicuro" come gli altri, perché richiede la dichiarazione della classe "visibile al client. Negli altri casi, è possibile dichiarare e definire la classe Imp nel file di implementazione, nascondendolo dai client.

Quindi è possibile esporre l'interfaccia che si sceglie. Naturalmente, i clienti possono ancora accedere ai membri se lo desiderano realmente tramite il runtime. Questo sarebbe più facile per loro fare in modo sicuro con il tipo ObjC Imp - il runtime objc non supporta la semantica C++ per i membri, quindi i client chiederebbero UB (IOW è tutto il POD al runtime).

Il costo di runtime per l'implementazione ObjC è scrivere un nuovo tipo, creare una nuova istanza Imp per ogni istanza e una buona quantità di raddoppiamento della messaggistica.

Il tipo C++ costa praticamente nulla, a parte l'allocazione (variante B).

opzione E

Altri approcci spesso si dissociano Ivars dalle interfacce. Mentre questa è una buona cosa, è anche molto inusuale per i tipi ObjC. I tipi/disegni ObjC spesso mantengono strette relazioni con i loro ivar e con i loro accessor, quindi dovrai affrontare la resistenza di altri sviluppatori.

Problemi correlati