Domanda

Come sviluppatore Java che sta leggendo la documentazione di Apple Objective-C 2.0: mi chiedo cosa " inviare un messaggio a zero " significa - per non parlare di come sia effettivamente utile. Tratto da un estratto della documentazione:

  

Esistono diversi motivi in ??Cocoa   che approfittano di questo fatto. Il   valore restituito da un messaggio a zero   può anche essere valido:

     
      
  • Se il metodo restituisce un oggetto, qualsiasi tipo di puntatore, qualsiasi scalare intero   di dimensioni inferiori o uguali a   sizeof (void *), un float, un double, a   doppio lungo o lungo lungo, quindi a   il messaggio inviato a zero restituisce 0.
  •   
  • Se il metodo restituisce una struttura, come definito dalla funzione ABI di Mac OS X.   Chiama la Guida per essere restituito   registra, quindi un messaggio inviato a zero   restituisce 0,0 per ogni campo in   struttura dati. Altri dati di struttura   i tipi non saranno riempiti con zeri.
  •   
  • Se il metodo restituisce qualcosa di diverso dal valore di cui sopra   digita il valore restituito di un messaggio   inviato a zero non è definito.
  •   

Java ha reso il mio cervello incapace di grokking la spiegazione sopra? O c'è qualcosa che mi manca che lo renderebbe chiaro come il vetro?

Mi viene in mente l'idea di messaggi / destinatari in Objective-C, sono semplicemente confuso su un ricevitore che sembra essere zero .

È stato utile?

Soluzione

Beh, penso che possa essere descritto usando un esempio molto ingegnoso. Supponiamo che tu abbia un metodo in Java che stampa tutti gli elementi in una ArrayList:

void foo(ArrayList list)
{
    for(int i = 0; i < list.size(); ++i){
        System.out.println(list.get(i).toString());
    }
}

Ora, se chiami quel metodo in questo modo: someObject.foo (NULL); probabilmente otterrai una NullPointerException quando tenta di accedere a list, in questo caso nella chiamata a list.size (); Ora, probabilmente non avresti mai chiamato someObject.foo (NULL) con il valore NULL del genere. Tuttavia, potresti aver ottenuto ArrayList da un metodo che restituisce NULL se si verifica un errore che genera ArrayList come someObject.foo (otherObject.getArrayList ());

Naturalmente, avrai anche problemi se fai qualcosa del genere:

ArrayList list = NULL;
list.size();

Ora, in Objective-C, abbiamo il metodo equivalente:

- (void)foo:(NSArray*)anArray
{
    int i;
    for(i = 0; i < [anArray count]; ++i){
        NSLog(@"%@", [[anArray objectAtIndex:i] stringValue];
    }
}

Ora, se abbiamo il seguente codice:

[someObject foo:nil];

abbiamo la stessa situazione in cui Java produrrà una NullPointerException. L'oggetto zero sarà accessibile per primo in [conteggio array] Tuttavia, invece di lanciare una NullPointerException, Objective-C restituirà semplicemente 0 in conformità con le regole precedenti, quindi il ciclo non verrà eseguito. Tuttavia, se impostiamo il ciclo in modo che venga eseguito un determinato numero di volte, inviamo prima un messaggio a anArray su [anArray objectAtIndex: i]; Anche questo restituirà 0, ma poiché objectAtIndex: restituisce un puntatore e un puntatore su 0 è zero / NULL, NSLog verrà passato zero ogni volta attraverso il ciclo. (Sebbene NSLog sia una funzione e non un metodo, stampa (null) se viene passato un NSString nullo.

In alcuni casi è meglio avere una NullPointerException, poiché puoi dire subito che qualcosa non va nel programma, ma a meno che tu non rilevi l'eccezione, il programma andrà in crash. (In C, il tentativo di dereferenziare NULL in questo modo provoca l'arresto anomalo del programma.) In Objective-C, causa invece un comportamento di runtime probabilmente errato. Tuttavia, se si dispone di un metodo che non si interrompe se restituisce 0 / nil / NULL / una struttura azzerata, questo evita di dover controllare per assicurarsi che l'oggetto o i parametri siano nulli.

Altri suggerimenti

Un messaggio a nil non fa nulla e restituisce nil , Nil , NULL , 0 o 0.0 .

Tutti gli altri post sono corretti, ma forse è il concetto che è la cosa importante qui.

Nelle chiamate al metodo Objective-C, qualsiasi riferimento ad oggetto che può accettare un selettore è una destinazione valida per quel selettore.

Ciò consente di risparmiare MOLTO di " l'oggetto target di tipo X? " codice - fintanto che l'oggetto ricevente implementa il selettore, fa assolutamente nessuna differenza che classe è! nil è un oggetto NSO che accetta qualsiasi selettore - semplicemente non fa nulla. Questo elimina un sacco di "verifica la presenza di zero, non inviare il messaggio se vero" anche il codice. (Il concetto di "se lo accetta, lo implementa" è anche ciò che consente di creare protocolli , che sono in qualche modo simili alle interfacce Java: una dichiarazione che se una classe implementa i metodi dichiarati, allora è conforme al protocollo.)

La ragione di ciò è eliminare il codice scimmia che non fa altro che mantenere felice il compilatore. Sì, ottieni l'overhead di un'altra chiamata di metodo, ma risparmi tempo del programmatore , che è una risorsa molto più costosa del tempo della CPU. Inoltre, stai eliminando più codice e più complessità condizionale dalla tua applicazione.

Chiarire per i downvoter: potresti pensare che questo non sia un buon modo di procedere, ma è come viene implementato il linguaggio ed è il linguaggio di programmazione raccomandato in Objective-C (vedi la programmazione di Stanford per iPhone lezioni).

Ciò significa che il runtime non produce un errore quando objc_msgSend viene chiamato sul puntatore zero; restituisce invece un valore (spesso utile). I messaggi che potrebbero avere un effetto collaterale non fanno nulla.

È utile perché la maggior parte dei valori predefiniti sono più appropriati di un errore. Ad esempio:

[someNullNSArrayReference count] => 0

Cioè, zero sembra essere l'array vuoto. Nascondere un riferimento NSView zero non fa nulla. Pratico, eh?

Nella citazione dalla documentazione, ci sono due concetti separati: forse potrebbe essere meglio se la documentazione lo rendesse più chiaro:

  

Esistono diversi modelli in Cocoa che sfruttano questo fatto.

     

Il valore restituito da un messaggio a zero può anche essere valido:

Il primo è probabilmente più pertinente qui: in genere essere in grado di inviare messaggi a nil rende il codice più semplice - non è necessario controllare valori null ovunque. L'esempio canonico è probabilmente il metodo accessor:

- (void)setValue:(MyClass *)newValue {
    if (value != newValue) { 
        [value release];
        value = [newValue retain];
    }
}

Se l'invio di messaggi a nil non fosse valido, questo metodo sarebbe più complesso - dovresti avere due controlli aggiuntivi per assicurarti value e newValue non sono zero prima di inviare loro messaggi.

Quest'ultimo punto (che i valori restituiti da un messaggio a nil sono generalmente validi), tuttavia, aggiunge un effetto moltiplicatore al primo. Ad esempio:

if ([myArray count] > 0) {
    // do something...
}

Questo codice di nuovo non richiede un controllo per i valori nil e scorre naturalmente ...

Detto questo, l'ulteriore flessibilità di poter inviare messaggi a nil ha un costo. C'è la possibilità che a un certo punto scriverai codice che fallisce in un modo particolare perché non hai tenuto conto della possibilità che un valore potrebbe essere nil .

Da Greg Parker di site :

Se si esegue LLVM Compiler 3.0 (Xcode 4.2) o successivo

Messages to nil with return type | return
Integers up to 64 bits           | 0
Floating-point up to long double | 0.0
Pointers                         | nil
Structs                          | {0}
Any _Complex type                | {0, 0}

Spesso significa non dover cercare oggetti nulli ovunque per sicurezza - in particolare:

[someVariable release];

o, come notato, vari metodi di conteggio e lunghezza restituiscono tutti 0 quando hai un valore zero, quindi non devi aggiungere controlli extra per zero dappertutto:

if ( [myString length] > 0 )

o questo:

return [myArray count]; // say for number of rows in a table

Non pensare a " il ricevitore è nullo " ;; Sono d'accordo, è piuttosto strano. Se stai inviando un messaggio a zero, non c'è ricevitore. Stai solo inviando un messaggio a nulla.

Come affrontarlo è una differenza filosofica tra Java e Objective-C: in Java, questo è un errore; in Objective-C, è una no-op.

I messaggi ObjC che vengono inviati a zero e i cui valori di ritorno hanno dimensioni maggiori di sizeof (void *) producono valori non definiti sui processori PowerPC. Inoltre, questi messaggi causano la restituzione di valori indefiniti in campi di strutture le cui dimensioni sono superiori a 8 byte anche sui processori Intel. Vincent Gable lo ha descritto bene nel suo post sul blog

Non credo che nessuna delle altre risposte lo abbia menzionato chiaramente: se sei abituato a Java, dovresti tenere presente che mentre Objective-C su Mac OS X ha il supporto per la gestione delle eccezioni, è una funzione del linguaggio opzionale che può essere attivato / disattivato con un flag di compilatore. La mia ipotesi è che questo progetto di "invio di messaggi a zero sia sicuro " precede l'inclusione del supporto per la gestione delle eccezioni nella lingua ed è stato fatto con un obiettivo simile in mente: i metodi possono restituire nil per indicare errori, e poiché l'invio di un messaggio a nil di solito restituisce nil a sua volta, ciò consente all'indicazione di errore di propagarsi attraverso il codice in modo da non doverlo verificare ad ogni singolo messaggio. Devi solo verificarlo nei punti in cui è importante. Personalmente ritengo che la propagazione delle eccezioni e la gestione siano un modo migliore per raggiungere questo obiettivo, ma non tutti possono essere d'accordo. (D'altra parte, ad esempio, non mi piace il requisito di Java di dichiarare quali eccezioni potrebbe generare un metodo, il che spesso ti costringe a sinteticamente propagare dichiarazioni di eccezione in tutto il codice; ma questo è un altro discussione.)

Ho pubblicato una risposta simile, ma più lunga, alla domanda correlata " Sta affermando che ogni creazione di oggetti è riuscita in modo necessario nell'Obiettivo C? " se vuoi maggiori dettagli.

C rappresenta nulla come 0 per i valori primitivi e NULL per i puntatori (che è equivalente a 0 in un contesto di puntatore).

Objective-C si basa sulla rappresentazione di nulla di C aggiungendo zero. zero è un puntatore a nulla. Sebbene siano semanticamente distinti da NULL, sono tecnicamente equivalenti tra loro.

Gli oggetti NSO appena allocati iniziano la vita con il loro contenuto impostato su 0. Ciò significa che tutti i puntatori che l'oggetto ha su altri oggetti iniziano come zero, quindi non è necessario, ad esempio, impostare self. (associazione) = zero in metodi init.

Il comportamento più notevole di zero, tuttavia, è che può essere inviato messaggi ad esso.

In altre lingue, come C ++ (o Java), ciò causerebbe l'arresto anomalo del programma, ma in Objective-C, invocare un metodo su zero restituisce un valore zero. Questo semplifica notevolmente le espressioni, in quanto ovvia alla necessità di verificare lo zero prima di fare qualsiasi cosa:

// For example, this expression...
if (name != nil && [name isEqualToString:@"Steve"]) { ... }

// ...can be simplified to:
if ([name isEqualToString:@"Steve"]) { ... }

Essere consapevoli di come funziona zero in Objective-C consente a questa comodità di essere una funzionalità e non un bug in agguato nella tua applicazione. Assicurati di evitare i casi in cui i valori zero sono indesiderati, controllando e restituendo in anticipo per fallire silenziosamente o aggiungendo un NSParameterAssert per generare un'eccezione.

Fonte: http://nshipster.com/nil/ https://developer.apple.com/ library / ios / # documentazione / cacao / concettuale / objectivec / Chapters / ocObjectsClasses.html (Invio di un messaggio a zero).

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