2009-08-06 6 views
24

Ich benutze ein Framework, das 'ClassA' definiert und verwendet, eine Unterklasse von NSObject. Ich möchte einige Variablen und Funktionalität hinzufügen, also habe ich natürlich 'ClassB', eine Unterklasse von 'ClassA' erstelltWie man 'Klasse A' in seine Unterklasse 'Klasse B' umsetzt - Objective-C

Jetzt ist mein Problem das. Viele der Methoden in diesem Framework geben Instanzen von 'ClassA' zurück, die ich in meine Unterklasse umwandeln möchte.

Zum Beispiel nimmt diese Methode:

- (ClassA *)doSomethingCool:(int)howCool 

Jetzt in meinem Code ich versuche dies:

ClassB * objB; 
objB = (ClassB *)doSomethingCool(10); 

NSLog(@"objB className = %@", [objB className]); 

Das läuft ganz gut. Keine Kompilier- oder Laufzeitfehler oder irgendetwas. Aber was ist wirklich seltsam für mich ist die Ausgabe:

>> "objB className = ClassA" 

Das Casting offensichtlich fehlgeschlagen. Nicht sicher, was zu diesem Zeitpunkt passiert ist ... objB wird als 'ClassB' eingegeben, aber es ist className 'ClassA' und es wird nicht auf 'ClassB' Methoden reagieren.

Nicht sicher, wie das möglich ist ... Wer weiß, was ich hier falsch mache?

fand ich eine ähnliche Position, die von genau das Gegenteil, was ich frage here

+0

Können Sie zeigen, wie Sie ClassB in doSomethingCOol erstellt haben? – stefanB

Antwort

22

Das Umwandeln von Objektvariablen in Objective-C ist normalerweise ein Fehler (es gibt ein paar Fälle, in denen es richtig ist, aber nie für so etwas). Beachten Sie, dass Sie kein Objekt umwandeln - Sie werfen einen Zeiger auf ein Objekt. Sie haben dann einen Zeiger vom Typ ClassB*, aber es verweist immer noch auf die gleiche Instanz von ClassA. Das Ding, auf das gezeigt wird, hat sich überhaupt nicht geändert.

Wenn Sie Instanzen von ClassA wirklich in ClassB konvertieren möchten, müssen Sie eine ClassB-Konstruktormethode schreiben, die eine ClassB-Instanz aus einem ClassA erstellen kann. Wenn Sie Instanzvariablen wirklich hinzufügen müssen, ist dies möglicherweise die beste Wahl.

Wie Jason schon sagte, ist es oft eine gute Idee, zuerst eine Kategorie auszuprobieren.

+0

Erinnern-die von der Methode zurückgegebene Instanz von 'ClassA' kann _really_ ein Objekt vom Typ' ClassB' sein, das auf Nachrichten reagiert, die von 'ClassB'-Objekten verstanden werden. – mipadi

+0

Obwohl ich nun die Frage noch einmal gelesen habe, nicht genau in diesem Szenario, da Nick anscheinend ein Framework eines Drittanbieters verwendet, das sowieso nichts über 'ClassB' weiß. – mipadi

+0

Unabhängig davon, was es ist, ist es ein Programmierfehler, anzunehmen, dass es etwas anderes ist als das, was es eingegeben hat. Die einzige wirkliche Ausnahme sind APIs, bei denen Basistypen (wie id oder NSObject) speziell verwendet werden, weil der Aufrufer genau wissen soll, welchen Typ das Objekt hat (oder dafür verantwortlich ist, es programmatisch herauszufinden) - wie in Sammlungen und Aktionsnachrichten. –

18

Sie können nicht nur eine Superklasse zu seiner Unterklasse werfen. Es implementiert keine Ihrer hinzugefügten Variablen/Eigenschaften oder Methoden. Sobald Sie versuchen, es mit einer Methode zu signieren, die Sie für Ihre Unterklasse definieren, erhalten Sie eine Laufzeitausnahme und Ihre Anwendung wird beendet. Sie können nur in eine Richtung sicher werfen: von spezifischer zu allgemeiner. Das heißt, Sie können unseren ClassB sicher in eine ClassA umwandeln, indem Sie nur die Methoden und Eigenschaften von ClassA verwenden, aber nicht umgekehrt.

Denken Sie daran: Sie haben ein Auto (die Elternklasse) und ein FourDoorSedan (die Unterklasse). Jedes Auto hat einen Motor und zwei Türen. Nehmen wir an, Sie bekommen irgendwo ein Auto, und das ist wirklich alles, was Sie darüber wissen. Sie sagen dem Betreiber, dass das Auto ein FourDoorSedan ist, aber es stellt sich heraus, dass es nicht ist. Also, wenn der Operator so etwas macht: openBackPassengerDoor, was passiert? Es gibt nur zwei Türen !! Es ist das gleiche hier.

Wenn Sie ClassA nur ein wenig Funktionalität hinzufügen möchten, schauen Sie sich die Objective-C-Kategorien an, sie sind wahrscheinlich, was Sie wollen und kein Casting wird benötigt. Lesen Sie die Dokumentation jedoch sorgfältig, da sie nicht ohne Vorbehalte sind.

+4

Sie können casten, wenn 'doSomethingCool:' wirklich _did_ ein Objekt vom Typ 'ClassB' zurückgibt. Sein Rückgabetyp ist "ClassA", aber er kann tatsächlich ein Objekt von "ClassB" zurückgeben (da "ClassB" "-a" "ClassA" ist). Wenn Sie _know_ 'doSomethingCool:' ein Objekt vom Typ 'ClassB' zurückgegeben haben (das Sie mit' [objB isKindOfClass: [ClassB class]] überprüfen können, wenn Sie sich nicht sicher sind), können Sie eine Nachricht senden und senden zu 'objB', um es zu veranlassen, eine' ClassB'-Methode durchzuführen. Sie können 'ansjectsToSelector' auch verwenden, um _really_ sure zu sein. – mipadi

+0

Obwohl die Frage erneut gelesen wird, wenn das Framework ein Framework eines Drittanbieters ist, das gar nichts über 'ClassB' weiß (und nicht von Nick geändert wurde), dann werden Objekte nicht wirklich zurückgegeben sind sowieso Instanzen von 'ClassB'. – mipadi

+0

Genau, es ist ein 3-Parteien-Framework, so dass CLassB definitiv nicht zurückgegeben wird. Außerdem wissen wir * sicher *, dass es ClassB nicht zurückgibt, da [objB class] KlasseA zurückgibt. –

0

Was passiert, wenn Sie eine der Methoden der Unterklasse für das zurückgegebene Objekt aufrufen?

Obj-C ist ziemlich frei und verlieren mit Typen, Sie müssen nicht einmal den Typ vor der Hand, z. Sie könnten alle Ihre ClassB-Referenzen auf id ändern. Ich weiß, es ist sicherer zu wissen, dass Sie den erwarteten Typ haben, aber wenn Ihr Code korrekt ist, sollte das keine Rolle spielen.

+0

Sein Code ist nicht korrekt. Er bekommt eine ClassA und tut nur so, als wäre es ein ClassB, weil er ClassB bevorzugt. Deshalb kann man nicht willkürlich etwas aufwerten. In Ihrem Beispiel wird seine Anwendung in Zeile 3 abstürzen. –

+0

Ich nahm an, dass er irgendwo seinen ClassB bestanden hatte und der Rahmen es ihm zurückgab. In diesem Fall wäre der Code in Ordnung. Wenn dies nicht der Fall ist, macht die Frage keinen Sinn. Du kannst eine Klasse nicht magisch in eine andere verwandeln. Kategorien funktionieren auch nicht. – Henry

+0

Ich denke, er ist nur verwirrt über das Casting, basierend auf dem, was er geschrieben hat, glaube ich nicht, dass er irgendetwas weitergegeben hat. Wenn er es getan hätte, hätte er das zurückbekommen, kein echtes Beispiel von ClassA. Er wollte jedoch "ClassA einige Funktionalität hinzufügen", für die genau Kategorien sind. –

5

Wenn Sie nur eine Methode zu vorhandenen Objekten hinzufügen möchten, ist Unterklasse nicht der richtige Weg. Sie können Methoden zu vorhandenen Klassen (und ihren Instanzen) hinzufügen, indem Sie eine Sprachfunktion namens category verwenden.

Beispiel:

//"ClassA+doSomethingCool.h" 
@interface ClassA (doSomethingCool) 
- (ClassA *)doSomethingCool:(int)howCool; 
@end 


//"ClassA+doSomethingCool.m" 
@implementation ClassA (doSomethingCool) 
- (ClassA *)doSomethingCool:(int)howCool 
{ 

} 
@end 
2

Casting konvertiert nicht von einer Art von Objekt zu einem anderen. Sie können eine Bibliothek nicht zwingen, den Objekttyp zu ändern und einen anderen Objekttyp zu erstellen, es sei denn, es wird ein Fabrikmuster verwendet.