Domanda

Ho diversi anni di esperienza in Obj-c e Cocoa, ma proprio ora sto tornando su di esso e sui progressi di Obj-C 2.0 ecc.

Sto cercando di orientare il runtime moderno e dichiarare le proprietà, ecc. Una cosa che mi confonde un po 'è la capacità nel runtime moderno di creare gli iVar in modo implicito. E ovviamente questo implica che nel tuo codice dovresti sempre usare self.property per accedere al valore.

Tuttavia, nei metodi init * e dealloc (supponendo che tu non stia utilizzando GC) dovremmo utilizzare direttamente iVar (nel runtime corrente).

Quindi le domande sono:

  1. Dovremmo usare gli accessori di proprietà in init * e dealloc con Modern Runtime?

  2. Se sì , perché è diverso? È solo perché il compilatore non può vedere l'iVar?

  3. Se devo sovrascrivere un accessor, posso comunque accedere a quell'iVar che verrà definito in fase di esecuzione o devo definire un iVar effettivo che verrà quindi utilizzato dal runtime?

  4. Ancora una volta, se posso accedere all'iVar sintetizzato , perché non posso continuare a farlo per i metodi init * e dealloc?

Ho letto i documenti più volte, ma mi sono sembrati un po 'vaghi su tutto questo e voglio essere sicuro di averlo capito bene per decidere come voglio continuare a scrivere codice.

Spero che le mie domande siano chiare.


Riepilogo rapido dei test :

  1. Se non si dichiarano gli ivar legacy, il compilatore è completamente infelice

  2. Se usi #ifndef __OBJC2__ intorno a ivar nel compilatore legacy è felice e puoi usare sia ivar direttamente che come proprietà

  3. Nel runtime moderno, è possibile lasciare Ivar non definito e accedere come proprietà

  4. Nel runtime moderno, il tentativo di accedere a ivar direttamente senza dichiarazione genera errori durante la compilazione

  5. @private la dichiarazione di ivar, ovviamente, consente l'accesso diretto a ivar, sia in eredità che in moderno

Non ti dà davvero un modo pulito per andare avanti adesso?

È stato utile?

Soluzione

Nel compilatore corrente (OS X 10.5 / GCC 4.0.1), non è possibile accedere direttamente agli ivar sintetizzati in runtime. Greg Parker, uno degli ingegneri di runtime di OS X lo ha messo in questo modo nell'elenco dei cacao-dev (12 marzo 2009):

  

Non è possibile nel compilatore corrente. UN   compilatore futuro dovrebbe risolverlo. Uso   espliciti @private ivars in   frattempo. Un ivar @private non dovrebbe   essere considerato parte del contratto -   ecco cosa significa @private, applicato   dagli avvisi del compilatore e dal linker   errori.

     

E perché non c'è un modo per farlo   dichiarare esplicitamente le variabili di istanza   nel file .m per il nuovo runtime?

     

Tre motivi: (1) ce ne sono alcuni   dettagli di design non banali per funzionare   fuori, (2) ore di compilatore-ingegnere sono   sono limitati e (3) @private ivars   generalmente abbastanza buono.

Quindi, per ora è necessario usare la notazione a punti per accedere alle proprietà, anche in init e dealloc . Ciò è contrario alla migliore pratica di utilizzare gli ivar direttamente in questi casi, ma non c'è modo di aggirarlo. Trovo che la facilità di utilizzo degli ivar sintetizzati in runtime (e dei vantaggi in termini di prestazioni) sia superiore a questo nella maggior parte dei casi. Dove è necessario accedere direttamente all'ivar, è possibile utilizzare un ivar @private come suggerisce Greg Parker (non c'è nulla che ti impedisca di mescolare ivar esplicitamente dichiarati e sintetizzati in fase di esecuzione).

Aggiorna Con OS X 10.6, il runtime a 64 bit consente l'accesso diretto agli ivar sintetizzati tramite self- > ivar .

Altri suggerimenti

Poiché le variabili di istanza stesse possono essere sintetizzate solo nel runtime moderno (e devono essere dichiarate nell'interfaccia @ sotto 32-bit o pre-Leopard), è più sicuro / più portatile dichiarare anche l'ivar

  
      
  • Dovremmo usare gli accessori di proprietà in init * e dealloc con Modern Runtime?
  •   

La mia regola empirica è " possibilmente " per -init * e " di solito non " per -dealloc .

Quando si inizializza un oggetto, si desidera assicurarsi di copiare / conservare correttamente i valori per gli ivar. A meno che il setter della proprietà non abbia qualche effetto collaterale che lo rende inappropriato per l'inizializzazione, riutilizzare definitivamente l'astrazione fornita dalla proprietà.

Quando si dealloca un oggetto, si desidera rilasciare qualsiasi oggetto Ivar, ma non memorizzarne di nuovi. Un modo semplice per farlo è impostare la proprietà su zero ( myObject.myIvar = nil ), che sostanzialmente chiama [myObject setMyIvar: nil] . Poiché i messaggi a zero vengono ignorati, non vi è alcun pericolo in questo. Tuttavia, è eccessivo quando [rilascio myIvar]; di solito è tutto ciò che serve. In generale, non utilizzare la proprietà (o direttamente il setter) in situazioni in cui la deallocazione dovrebbe comportarsi diversamente dall'impostazione della variabile.

Riesco a comprendere l'argomento di eJames contro l'utilizzo di accessor di proprietà in init / dealloc, ma il rovescio della medaglia è che se si modifica il comportamento della proprietà (ad esempio, si cambia da conserva a copia o si assegna semplicemente senza conservazione) e non t usalo in init, o viceversa, anche il comportamento può non essere sincronizzato. Se l'inizializzazione e la modifica di un ivar devono agire allo stesso modo, utilizzare l'accesso alla proprietà per entrambi.

  
      
  • In tal caso, perché è diverso? È solo perché il compilatore non può vedere l'ivar?
  •   

Il runtime moderno gestisce le dimensioni e il layout della classe in modo più intelligente, motivo per cui è possibile modificare il layout degli ivar senza dover ricompilare le sottoclassi. È anche in grado di dedurre il nome e il tipo di ivar desiderato dal nome e dal tipo della proprietà corrispondente. La Guida alla programmazione runtime di Objective-C 2.0 ha più informazioni, ma ancora una volta, Non so quanto siano stati approfonditi i dettagli lì.

  
      
  • Se devo sovrascrivere un accessor, posso comunque accedere a quell'iVar che verrà definito in fase di esecuzione o devo definire un iVar effettivo che verrà quindi utilizzato dal runtime?
  •   

Non l'ho testato, ma credo che ti sia permesso accedere al nome ivar nel codice, dal momento che in realtà deve essere creato. Non sono sicuro che il compilatore si lamenterà, ma immagino che dal momento che ti consentirà di sintetizzare l'ivar senza lamentarti, è anche abbastanza intelligente da sapere sull'ivar sintetizzato e farti riferimento per nome.

  
      
  • Ancora una volta, se posso accedere all'iVar sintetizzato, perché non posso continuare a farlo per i metodi init * e dealloc?
  •   

Dovresti essere in grado di accedere alla proprietà e / o ivar in qualsiasi momento dopo che l'istanza è stata allocata.

C'è un'altra domanda SO con informazioni simili, ma non è del tutto duplicata.

La linea di fondo, dalla Objective-C 2.0 , e citato da La risposta di Mark Bessey è la seguente:

  

Esistono differenze nel comportamento che dipendono dal runtime (vedi anche & # 8220; Differenze di runtime & # 8221;):

     

Per i runtime legacy, le variabili di istanza devono già essere dichiarate nel blocco @interface. Se esiste una variabile di istanza con lo stesso nome e tipo compatibile della proprietà, viene utilizzata & # 8212; in caso contrario, viene visualizzato un errore del compilatore.

     

Per i runtime moderni, le variabili di istanza vengono sintetizzate secondo necessità. Se esiste già una variabile di istanza con lo stesso nome, viene utilizzata.

La mia comprensione è la seguente:

Non dovresti usare gli accessor delle proprietà nei metodi init * e dealloc , per gli stessi motivi per cui non dovresti usarli nel runtime legacy: ti lascia aperto a potenziali errori se successivamente si sovrascrivono i metodi della proprietà e si finisce per fare qualcosa che non dovrebbe essere fatto in init * o dealloc .

Dovresti essere in grado di sintetizzare sia ivar che sovrascrivendo i metodi delle proprietà come segue:

@interface SomeClass
{
}
@property (assign) int someProperty;
@end

@implementation SomeClass
@synthesize someProperty; // this will synthesize the ivar
- (int)someProperty { NSLog(@"getter"); return someProperty; }
- (void)setSomeProperty:(int)newValue
{
    NSLog(@"setter");
    someProperty = newValue;
}
@end

Il che mi porta a pensare che saresti in grado di accedere all'ivar sintetizzato anche nei tuoi metodi init * e dealloc . L'unica cosa che mi è venuta in mente è che la riga @synthesize potrebbe dover venire prima delle definizioni del tuo init * e dealloc nel file sorgente.

Alla fine, poiché avere gli ivar dichiarati nell'interfaccia funziona ancora, questa è ancora la scommessa più sicura.

Sto riscontrando lo stesso problema. Il modo in cui sto lavorando per non poter accedere alle variabili di istanza sintetizzate è il seguente:

intestazione pubblica

@interface MyObject:NSObject {
}
@property (retain) id instanceVar;
@property (retain) id customizedVar;
@end

intestazione / implementazione privati ??

@interface MyObject()
@property (retain) id storedCustomizedVar;
@end

@implementation MyObject
@synthesize instanceVar, storedCustomizedVar;
@dynamic customizedVar;

- customizedVar {
  if(!self.storedCustomizedVar) {
    id newCustomizedVar;
    //... do something
    self.storedCustomizedVar= newCustomizedVar;
  }
  return self.storedCustomizedVar;
}

- (void) setCustomizedVar:aVar {
  self.storedCustomizedVar=aVar;
}

@end

Non è così elegante, ma almeno mantiene pulito il mio file di intestazione pubblico.

Se usi KVO devi definire customVar come chiave dipendente di storedCustomizedVar.

Sono relativamente nuovo su Obj-C (ma non sulla programmazione) e sono stato anche confuso da questo argomento.

L'aspetto che mi preoccupa è che sembra relativamente facile usare inavvertitamente iVar invece della proprietà. Ad esempio, scrivendo:

myProp = someObject;

anziché

self.myProp = someObject;

Certamente questo è "utente" errore, ma sembra ancora abbastanza facile eseguirlo accidentalmente in alcuni codici e per una proprietà mantenuta o atomica potrebbe presumibilmente portare a problemi.

Idealmente preferirei essere in grado di ottenere il runtime per applicare alcuni pattern al nome della proprietà durante la generazione di qualsiasi iVar. Per esempio. anteponi sempre loro con " _ " ;.

In pratica al momento lo sto facendo manualmente - dichiarando esplicitamente i miei ivar e dando deliberatamente loro nomi diversi dalle proprietà. Uso un prefisso "m" vecchio stile, quindi se la mia proprietà è "myProp", il mio iVar sarà "mMyProp". Quindi uso @synthesize myProp = mMyProp per associare i due.

Lo ammetto un po 'goffo e un po' di digitazione extra, ma mi sembra che valga la pena chiarire un po 'più chiaramente il codice. Ovviamente posso ancora sbagliarmi e digitare mMyProp = someObject, ma spero che il prefisso 'm' mi avviserà del mio errore.

Sarebbe molto più bello se potessi semplicemente dichiarare la proprietà e lasciare che il compilatore / runtime faccia il resto, ma quando ho un sacco di codice il mio istinto mi dice che commetterò errori in questo modo se devo ancora seguire le regole manuali per init / dealloc.

Naturalmente ci sono anche molte altre cose che posso fare di sbagliato ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top