Sottoclasse e casting nell'obiettivo C
-
06-07-2019 - |
Domanda
Oggi ho riscontrato uno strano problema. Ho creato una sottoclasse di UIView e ho aggiunto solo 1 metodo al codice modello fornito da xcode.
@interface FloatView : UIView {
}
- (void)floatTest:(CGFloat)x;
@end
- (void)floatTest:(CGFloat)x {
NSLog(@"float was %f", x);
}
Quindi nella mia appDelegate avevo un codice come questo:
UIView *floatView = [[FloatView alloc] init];
[floatView floatTest:10.0f];
Abbastanza semplice, vero? Cosa dovrebbe stampare? Ho pensato che sarebbe qualcosa di simile a "10.0000", ma no, stampa "0,000000".
Ho lottato con questo per ore, cercando di capire cosa stavo facendo di sbagliato, e poi ho cambiato il codice nella mia appDelegate in
FloatView *floatView = [[FloatView alloc] init];
[floatView floatTest:10.0f];
Solo allora ha stampato il previsto "10.0000". Perché è così? Ho dichiarato FloatView come sottoclasse di UIView, non dovrei essere in grado di assegnare un oggetto FloatView a un puntatore UIView senza problemi?
Anche se floatView è stato dichiarato un puntatore a un UIView, è davvero un floatView e dovrebbe essere in grado di gestire il messaggio floatTest? Sono totalmente fuori base qui?
Soluzione
In realtà, il polimorfismo funziona come previsto. Se non avesse funzionato, non sarebbe stato stampato nulla (nel tuo esempio, 0,0000 è in fase di stampa). Il fatto è che l'istanza in realtà risponde al messaggio testFloat: 10.0f
, poiché il compilatore non può vedere staticamente la dichiarazione del metodo (poiché la classe UIView
non dichiara tale un metodo), presuppone che il tuo metodo prenda ...
come argomento e restituisca id
.
Quando CGFloat
viene passato a un metodo che prevede un numero variabile di argomenti ( ...
), viene promosso a double
. Pertanto, al metodo di ricezione viene passato un argomento double
e pensa che sia un float
e che non venga stampato correttamente.
È possibile verificare questo comportamento modificando la riga NSLog
in:
NSLog(@"%f", *(double*)&x);
Quando il compilatore invia il messaggio a FloatView *
anziché a UIView *
, può trovare la firma esatta del metodo. Può vedere che si aspetta davvero CGFloat
e non promuove l'argomento a double
. Di conseguenza, funziona correttamente.
Inoltre, se UIView *
contenesse la dichiarazione del metodo che ha preso un CGFloat
, il compilatore chiamerebbe il metodo in modo appropriato. Riassumendo, questo non è un problema di polimorfismo; è un problema di firma del metodo mancante.