• Apfeltalk ändert einen Teil seiner Allgemeinen Geschäftsbedingungen (AGB), das Löschen von Useraccounts betreffend.
    Näheres könnt Ihr hier nachlesen: AGB-Änderung
  • Viele hassen ihn, manche schwören auf ihn, wir aber möchten unbedingt sehen, welche Bilder Ihr vor Eurem geistigen Auge bzw. vor der Linse Eures iPhone oder iPad sehen könnt, wenn Ihr dieses Wort hört oder lest. Macht mit und beteiligt Euch an unserem Frühjahrsputz ---> Klick

Objective C Fragen

commander

Baldwins roter Pepping
Unvergessen
Registriert
25.02.04
Beiträge
3.206
Hi Leute,

ich hab mich jetzt mal endlich etwas intensiver mit ObjC beschäftigt, da ich nebenbei eine kleine Applikation bastele, und dafür einen Spotlight-Importer und andere Macspezifische Sachen benötige.

Also erstmal nix mit Java, später möchte ich aber mit der Cocoa-java-Bridge eine Component in eine Javaapplikation einbinden. Aber vorher muss alles fehlerfrei funktioniern.

Sehr beeindruckt bin ich von den Features, die XCode für ObjC - Cocoa Projekte so mitbringt - man kann sehr schnell einen Applikationsrahmen zusammenklickern, nicht schlecht.

Aber: Mein Erstaunen war recht groß, als ich mitbekommen habe, dass in ObjC alles dynamisch gebunden wird, und ich keine ClassCastExceptions oder anderes um die Ohren bekomme, falls der Typ nicht der erwartete ist. Des weiteren habe ich den Mund nicht mehr zubekommen, als ich bemerkt habe, dass ich auch NIL eine Message schicken kann, ohne dass das einen Fehler verursacht....

Ich bin als purer Javapapa natürlich sehr an solche Features gewöhnt und finde ihr Fehlen doch etwas seltsam.

Vielleicht kann mir auch jemand einen Tip geben, wie ich wenigsten für den Entwicklungszeitraum an der Laufzeit etwas drehen kann, um bessere Typsicherheit und v.a. sowas wie Nullpointer bekomme.... ich habe eigentlich keine Lust, überall alles und immer auf NIL zu überprüfen. Oder hat vielleicht jemand ein paar Best-Practice Tips für einen 'erfahrenen Anfänger' wie mich ??

Gruß,

.commander
 

commander

Baldwins roter Pepping
Unvergessen
Registriert
25.02.04
Beiträge
3.206
Des weiteren wären ein paar Tips zum Speichermanagement nicht schlecht, ich hab das letzte mal im letzten Jahrtausen etwas dealloziiert.... ;)

Gibts da neben dem Autoreleasepool noch andere Techniken?
 

below

Purpurroter Cousinot
Registriert
08.10.06
Beiträge
2.858
Also, dynamisches Binden und das senden von messages an nil sind gewünschte Features von Objective-C, die noch aus der Smalltalk Zeit kommen.

Um ein bischen Stärker zu typen, solltest Du "id" aus Deinem Code verbannen, und immer die echten Typen (NSString * etc.) verwenden.

Die nil messages sind eben gewollt. So erschlage ich mit

if ([string length] == 0) gleichzeitig den Fall, dass string == nil ist (messages an nil liefern nach Definition 0 zurück)

Auch kann ich bedenkenlos [myObject release] sagen, ohne vorher auf myObject == nil prüfen zu müssen.

Speichermanagement:

http://developer.apple.com/document...ip.html#//apple_ref/doc/uid/20000043-BEHDEDDB

Alex
 

commander

Baldwins roter Pepping
Unvergessen
Registriert
25.02.04
Beiträge
3.206
Danke!

Also, der Compiler merkt das ja, wenn der Typ nicht stimmt, id verwende ich sowieso garnicht. Aber wenn ich z.B. Objekte aus einer HashMap hole und sie dann typsicher casten will, so kommt erstmal kein Fehler, auch wenn der Typ nicht stimmt - u.U. kann ich eine Methode - sorry Message auch schicken, wenn die Klasse zufällig die gleiche Methode hat, also sowas wie getSize() und merke gar nicht, dass ich ein völlig anderes Objekt in der Hand habe als ich annehme.... dagegen kann man aber nichts machen, oder?

Das Argument mit [string length] und [myObject release] ist gut, jedoch komm ich trotzdem nicht darum herum, initial abzuchecken ob ich nicht nil in der hand halte, wenn ich eine manipulative Methode aufrufe..... was bekomme ich eigentlich zurück, wenn der Rückgabewert ein komplexes Objekt ist? Wieder nil?

Wie gehst Du denn da codemässig vor? In Java fange ich z.B. Nullparameter auch händisch ab, wenn ich Serviceklassen zur Verfügung stelle, wahrscheinlich ist es das beste, in ObjC das standardmässig in allen Methoden zu machen....

Gruß,

.commander
 

pepi

Cellini
Registriert
03.09.05
Beiträge
8.740
Gerade das dynmische Typing und Binding ist eine der Stärken von Objective-C. Verursacht weniger Compilerfehler als statisch gebundene Sprachen, dafür muß man eben explizites Unit-testing machen um Laufzeitfehler frühzeitig erkennen zu können.

Du kannst ein Objekt darüber befragen von welchem Typ es ist, bzw. ob es in einer bestimmten Vererbungshirarchie enthalten ist.
-(BOOL) isKindOf: class-object
-(BOOL) isMemberOf: class-object
-(BOOL) respondsTo: selector
+(BOOL) instancesRespondTo: selector
-(id) perform: selector

Ich kann zu dem Thema folgende Literatur wärmstens empfehlen (Ich ackere mich nämlich selbst gerade da durch.)
Programming in Objective-C
Gruß Pepi
 
Zuletzt bearbeitet:

commander

Baldwins roter Pepping
Unvergessen
Registriert
25.02.04
Beiträge
3.206
(...)dafür muß man eben explizites Unit-testing machen um Laufzeitfehler frühzeitig erkennen zu können.

Da sind wir gleich beim nächsten Thema: Ich entwickle eigentlich meist testdriven, wie mache ich das in ObjC? Gibt es da ein Framwork ähnlich wie JUnit? Oder automatisch generierte Testsuits?
 

below

Purpurroter Cousinot
Registriert
08.10.06
Beiträge
2.858
u.U. kann ich eine Methode - sorry Message auch schicken, wenn die Klasse zufällig die gleiche Methode hat, also sowas wie getSize() und merke gar nicht, dass ich ein völlig anderes Objekt in der Hand habe als ich annehme.... dagegen kann man aber nichts machen, oder?

Warum würdest Du etwas dagegen machen wollen? Cocoa selbst ist wundervoll orthagonal, im Gegensatz zu vielen anderen Frameworks: Wenn zwei Objekte auch "getSize" reagieren, dann reagieren sie auch in gleicher Weise darauf. Also wo ist das Problem? Ich kann an einen Array oder ein Dictionary "objectEnumerator" schicken, und bekomme dann, was ich erwarte.

Wenn Du willst, kannst Du den Typ der Objekte natürlich zur Laufzeit testen. Oder besser, Du fragst mit "respondsToSelector:", ob das Objekt die Methode auch versteht.
Das dynamische Laufzeitverhalten von ObjC ist so gewollt!

was bekomme ich eigentlich zurück, wenn der Rückgabewert ein komplexes Objekt ist? Wieder nil?
Ja, wieder nil

Wie gehst Du denn da codemässig vor?

Ich frage nur ab ob es nil ist, wenn ich das wirklich wissen muss. Bei solchen codezeilen ist es ja total egal:

if (object)
[object method];

Wie gesagt, es sei denn es ist ein Fehler, wenn das Object nicht gesetzt ist.

Bei dem Beispiel mit [string length] ist es mir auch egal, ob das Objekt jetzt nil ist, oder ein Leerstring. Wenn es in dem speziellen Fall einen Unterschied macht, frage ich es ab.

DANGER, WIL ROBINSON, DANGER!

Während Du bedenkenlos Messages an nil schicken darfst, kannst Du nicht ohne weiteres nil als Argument übergeben:

myObject = nil;
[myDict setValue:myObject forKey:mad:"The Key"];

schmeisst zur Laufzeit eine Exception, die Du handeln musst, oder vermeiden.

Alex
 

commander

Baldwins roter Pepping
Unvergessen
Registriert
25.02.04
Beiträge
3.206
Warum würdest Du etwas dagegen machen wollen? Cocoa selbst ist wundervoll orthagonal, im Gegensatz zu vielen anderen Frameworks: Wenn zwei Objekte auch "getSize" reagieren, dann reagieren sie auch in gleicher Weise darauf. Also wo ist das Problem? Ich kann an einen Array oder ein Dictionary "objectEnumerator" schicken, und bekomme dann, was ich erwarte.

Nun, wenn die beiden Objekte das selbe Interface implementieren, das die Methode getSize deklariert, dann ist das ja auch gewünscht. Ich bin es von Java, das da sehr restriktiv ist, so gewohnt dass ich ein Object, dessen Klasse nicht dieses Interface implementiert auch nicht auf das Interface casten kann...

Bei ObjC scheint mir aber nicht das Interface untersucht zu werden, sondern die Methode wird sozusagen 'by-name' aufgerufen. Nehmen wir mal an, die eine Methode gibt einen Integerwert zurück, und den erwarte ich auch, das Objekt das ich gerade in der Hand halte, hat ebenfalls eine getSize() - Methode, die jedoch ein komplexes Objekt liefert, das Größen für alle 3 Dimensionen enthält. Was passiert dann?
 

below

Purpurroter Cousinot
Registriert
08.10.06
Beiträge
2.858
Methode wird sozusagen 'by-name' aufgerufen
Richtig
Nehmen wir mal an, die eine Methode gibt einen Integerwert zurück, und den erwarte ich auch, das Objekt das ich gerade in der Hand halte, hat ebenfalls eine getSize() - Methode, die jedoch ein komplexes Objekt liefert, das Größen für alle 3 Dimensionen enthält. Was passiert dann?

Das soll eben nicht sein. Dann musst Du die Methode anders nennen.

Aber für mich ist das ein geringer Preis, den ich für die Möglichkeiten in Objective-C zahlen musst

Ausserdem seien wir doch mal ehrlich: Es tauchen doch nicht auf einmal "Rogue Objects" in Deinem Programm auf, die irgendwoher kommen.

Alex
 

Peter Maurer

Pommerscher Krummstiel
Registriert
16.03.04
Beiträge
3.077
Mein Erstaunen war recht groß, als ich mitbekommen habe, dass in ObjC alles dynamisch gebunden wird, und ich keine ClassCastExceptions oder anderes um die Ohren bekomme, falls der Typ nicht der erwartete ist.
Ja. Sehr praktisch, denn es macht Polymorphismus leichter. Idealerweise sind all die Typen willkommen, die die fragliche Botschaft verarbeiten koennen.

Des weiteren habe ich den Mund nicht mehr zubekommen, als ich bemerkt habe, dass ich auch NIL eine Message schicken kann, ohne dass das einen Fehler verursacht....
Auch das ist IRRSINNIG praktisch, wenn man sich mal dran gewoehnt hat. Erfundenes Beispiel:

[[schafe schwarzes] streicheWeissAn];

Wenn's kein schwarzes Schaf gibt, wird -streicheWeissAn nicht ausgefuehrt. Man kann es aber in jedem Fall so schreiben und spart damit eine vorherige Abfrage.

Vielleicht kann mir auch jemand einen Tip geben, wie ich wenigsten für den Entwicklungszeitraum an der Laufzeit etwas drehen kann, um bessere Typsicherheit und v.a. sowas wie Nullpointer bekomme.... ich habe eigentlich keine Lust, überall alles und immer auf NIL zu überprüfen.
Muss man u.U. gar nicht, s.o.

Was genau fuerchtest Du denn fuer Gefahren von ins Nirvana gesendeten Botschaften à la [nil machWas]?

Bei ObjC scheint mir aber nicht das Interface untersucht zu werden, sondern die Methode wird sozusagen 'by-name' aufgerufen.
Sehr richtig.

Nehmen wir mal an, die eine Methode gibt einen Integerwert zurück, und den erwarte ich auch, das Objekt das ich gerade in der Hand halte, hat ebenfalls eine getSize() - Methode, die jedoch ein komplexes Objekt liefert, das Größen für alle 3 Dimensionen enthält. Was passiert dann?
Sowas wuerde ich umgehen, in dem ich entweder sicherstelle, dass nur die richtigen Objekttypen abgefragt werden (z.B. [object isKindOfClass: [DesiredClass class]]) oder aber bei einer von beiden den Methodennamen aendere, wenn ich die Klasse selber in der Hand haette.

Apples Frameworks beinhalten erfahrungsgemaess kaum solche Missverstaendnisfallen.
 

tjp

Altgelds Küchenapfel
Registriert
07.07.04
Beiträge
4.059
Warum würdest Du etwas dagegen machen wollen?
Das Verhalten von Objective-C ist nur Kovention, und wie die Erfahrung vieler in größeren Softwareprojekten zeigt ist das schlecht - sehr schlecht. In der idealen Welt kommt man mit Konventionen zurecht, aber es gibt da noch die Realität.
Cocoa selbst ist wundervoll orthagonal, im Gegensatz zu vielen anderen Frameworks: Wenn zwei Objekte auch "getSize" reagieren, dann reagieren sie auch in gleicher Weise darauf. Also wo ist das Problem?
Das man das ohne Probleme umgehen kann. Niemand garantiert, daß getSize Teil eines bestimmten Protokolls ist, es ist bloß eine Konvention.
 

below

Purpurroter Cousinot
Registriert
08.10.06
Beiträge
2.858
Das Verhalten von Objective-C ist nur Kovention, und wie die Erfahrung vieler in größeren Softwareprojekten zeigt ist das schlecht - sehr schlecht. In der idealen Welt kommt man mit Konventionen zurecht, aber es gibt da noch die Realität.

Das man das ohne Probleme umgehen kann. Niemand garantiert, daß getSize Teil eines bestimmten Protokolls ist, es ist bloß eine Konvention.

In C (und C++, ObjC) ist insgesamt alles nur Konvention. Welche Sprache garantiert Dir denn die Rückgabewerte von Funktionen, Methoden oder Messages?
Wenn ich kurz überlege denke ich sogar, dass man in Objective-C viel leichter ein Contractual System aufbauen könnte als z.B. in Java.

Im übrigen, wenn Du nicht (id) verwendest, dann gibt Dir auch der Objektive C Compiler eine Warnung, dass Dein Objekt diese Nachricht so vielleicht nicht versteht.

Ich bin ein alter Smalltalker und CLOS Fan, daher finde ich ObjC wahrscheinlich so toll ;)

Alex
 

tjp

Altgelds Küchenapfel
Registriert
07.07.04
Beiträge
4.059
In C (und C++, ObjC) ist insgesamt alles nur Konvention. Welche Sprache garantiert Dir denn die Rückgabewerte von Funktionen, Methoden oder Messages?
Nein, das ist falsch. C++ (unter den Vorbehalt der C Altlasten) garantiert dies. Der Upcast erfolgt implizit, der Downcast muß explizit erfolgen, dabei wird überprüft, ob das referenzierte Objekt auch von der spezifizierten Klasse ist. Ist das nicht der Fall -> Exception.

Ich bin ein alter Smalltalker und CLOS Fan, daher finde ich ObjC wahrscheinlich so toll ;)

Alex
Mich stört an Objective-C das es weder Fisch noch Fleisch ist. Es hat nicht die LowLevel Stärken von Ada und C++ (Typsicherheit, OO Fähigkeiten auch bei LowLevel Funktionen und das mit sehr schnellen Code) und es bietet nicht den sauberen Ansatz von SmallTalk beim Programmieren im Großen, da hängt der Klotz C-Kompatibilität am Bein.
 

below

Purpurroter Cousinot
Registriert
08.10.06
Beiträge
2.858
Nein, das ist falsch. C++ (unter den Vorbehalt der C Altlasten) garantiert dies. Der Upcast erfolgt implizit, der Downcast muß explizit erfolgen, dabei wird überprüft, ob das referenzierte Objekt auch von der spezifizierten Klasse ist. Ist das nicht der Fall -> Exception.

Das waren nicht die Garantien, die ich meine, aber egal.

Wenn wir jedoch die C "Altlasten" weglassen dürfen, was ist dann Dein Problem? Da Objective-C single inheritance ist weiss ich, das jedes Object schonmal mindestens vom Typ id (NSObject). Und dann kann ich es fragen und entsprechend reagieren.

Warum es besser ist, wenn mir das Programm mit einer Exception wegfliegt verstehe ich nicht. Ausserdem habe ich den Threadstarter so verstanden, dass er Überprüfungen dieser Art zur Compilezeit, und nicht zur Laufzeit haben wollte.

Alex
 

tjp

Altgelds Küchenapfel
Registriert
07.07.04
Beiträge
4.059
Das waren nicht die Garantien, die ich meine, aber egal.
Wenn eine Funktion einen Zeiger auf "Protokollklasse" A erwartet, dann kannst Du nur Zeiger von Klassen an diese Funktion zuweisen, die von A abgeleitet sind. Nein, das ist nicht bloß eine Compiler Warning wie das bei Objective-C der Fall ist, daß ist ein fundamentaler Compile Error, weil es ein Typfehler ist, wenn die Prüfung beim Compilieren fehl schlägt.
Wenn wir jedoch die C "Altlasten" weglassen dürfen, was ist dann Dein Problem?
Ich denke nicht, daß Du weißt was ich meinte. In C++ darf man noch immer den C Cast verwenden, das liegt an der sehr weit gehenden Kompatiblität mit C. Aber für das C++ Programmieren selbst gibt es eigene C++ Casts. Wenn man mit Vererbungshierachien arbeitet, dann reicht der dynamic_cast aus, und dann ist immer garantiert, daß kein Unfug passiert.
Warum es besser ist, wenn mir das Programm mit einer Exception wegfliegt verstehe ich nicht.
Man macht eine explizite Überprüfung auf den Typ, und zwar dann wenn man das auf Grund der Vererbungshierachie nicht wissen kann, ob ein Objekt von einem bestimmten Typ ist. Schlägt das fehl, dann wirft C++ eine Exception. Die kann man behandeln und dann fliegt das Programm auch nicht weg.
Ausserdem habe ich den Threadstarter so verstanden, dass er Überprüfungen dieser Art zur Compilezeit, und nicht zur Laufzeit haben wollte.

Alex
Aus dieser Anwort schließe ich, daß Du den Unterschied zwischen Up- und Downcast nicht kennst. Da man dies weder in Objective-C noch in SmallTalk nutzt wundert mich das auch nicht.
 

below

Purpurroter Cousinot
Registriert
08.10.06
Beiträge
2.858
Nein, mir sagen upcast und downcast tatsächlich nichts.

Vielleicht sehe ich etwas falsch, aber irgendwie glaube ich weiterhin, dass ich in Objective-C wunderbar z.B. das Programming By Contract Prinzip anwenden könnte, wenn ich das wollte.

Und kann auch gut sein, das ich irgendwie das Problem nicht verstehe.

Alex
 

Peter Maurer

Pommerscher Krummstiel
Registriert
16.03.04
Beiträge
3.077
Wenn eine Funktion einen Zeiger auf "Protokollklasse" A erwartet, dann kannst Du nur Zeiger von Klassen an diese Funktion zuweisen, die von A abgeleitet sind. Nein, das ist nicht bloß eine Compiler Warning wie das bei Objective-C der Fall ist, daß ist ein fundamentaler Compile Error, weil es ein Typfehler ist, wenn die Prüfung beim Compilieren fehl schlägt.
Das hielte ich fuer eine falsche Sicherheit. In vielen Faellen kann der Compiler doch gar nicht wissen, was kommt. Beispiel:
Code:
[[sampleDictionary objectForKey: @"Feature"] doProtocolAction];

Zugegeben: Man koennte das ausfuehrlicher formulieren, ungefaehr so:
Code:
[(id <Protocol>)[sampleDictionary objectForKey: @"Feature"] doProtocolAction];

Und fuer Sicherheitsfanatiker:
Code:
id feature = [sampleDictionary objectForKey: @"Feature"];
if ([feature conformsToProtocol: @protocol(Protocol)]) [(id <Protocol>)feature doProtocolAction];

Objective C bzw. der entsprechende Compiler machen das nicht automatisch, stimmt. Aber das ist wieder eine dieser Geschmacksfragen: Mir gefallen die schlichte Schoenheit und die zwanglose Polymorphismusfaehigkeit der ersten Variante.

Und der Compiler kann -- wie gesagt -- ohnehin nicht immer wissen, was kommt. Das Ende vom Lied waere also so oder so ein Laufzeitfehler.

Man macht eine explizite Überprüfung auf den Typ, und zwar dann wenn man das auf Grund der Vererbungshierachie nicht wissen kann, ob ein Objekt von einem bestimmten Typ ist.
Das scheint mir eine zentrale Frage zu sein: Muss ich das wissen, will ich das wissen? Solange die Methode meiner Wahl funktioniert, bin ich zufrieden. Je weniger Code-Schnipsel A ueber Code-Schnipsel B wissen muss, desto besser.

Aus dieser Anwort schließe ich, daß Du den Unterschied zwischen Up- und Downcast nicht kennst. Da man dies weder in Objective-C noch in SmallTalk nutzt wundert mich das auch nicht.
Typischer Objective-C-Upcast, kommt oft vor:
Code:
NSDictionary *sampleDictionary = [NSMutableDictionary dictionaryWith...];

Das ist natuerlich verkuerzt dargestellt, meist wird das NSMutableDictionary von einer Methode/Funktion zurueckgegeben.

Mitlesenden Haarspaltern sei ausserdem zugestanden, dass gerade dieses Beispiel eigentlich komplizierter ist: NSCFDictionary, die tatsaechliche Klasse von NSDictionary-und NSMutableDictionary-Objekten, ist aus technischen Gruenden eine NSMutableDictionary-Subklasse. Ich hab' darueber mal ausfuehrlicher mit Ali Ozer und Jens Alfke konferiert, weil ich einen Fehler hatte, der ohne diese Annahme nicht erklaerbar war. Fuer 99,9% der Faelle und Programmierer funktioniert aber die "convenient fiction imposed by the API" (Alfke), NSMutableDictionary-Objekte gehoerten zu einer NSDictionary-Subklasse, besser.

Wenn Dir was dran liegt, kannst Du das auch so schreiben:
Code:
NSDictionary *sampleDictionary = (NSDictionary*)[NSMutableDictionary dictionaryWith...];

Insgesamt gibt Objective C Dir eben mehr Freiheiten und laesst Dich eindeutige Aufgaben mit weniger Formalien wie beispielsweise explizitem Casting loesen. Das macht den Code auch lesbarer, und das ist ja ohnehin eine der Staerken von Objective C.

Dafuer muss man in der Tat einen Preis bezahlen, indem man sich weniger von Compiler & Co bemuttern laesst und bei Bedarf die Kontrollen, die Du Dir -- m.E. teils unrealistischerweise (s.o.) -- vom Compiler wuenschst, selbst in die Laufzeit einbaut.