[Class class] is not equal [instance class]

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • [Class class] is not equal [instance class]

    Moin,

    ich hätte da gern eine Frage. ;)

    Aktuell hantiere ich mit einer Factory umher, die mir je nach Dateiname eine spezifische Instanz zurückgeben soll.
    Das läuft soweit auch recht fein.

    Der Test ist sinngemäß, wenn nicht gar wörtlich:

    Quellcode

    1. - (void)testKMLImporterCreation
    2. {
    3. [[self mockItem] setString:@"file:///dummy.kml" forType:@"public.file-url"];
    4. id importer = [MyImporterFactory importerForPasteboardItem:[self mockItem]];
    5. XCTAssertNotNil(importer, @"Importer creation failed");
    6. XCTAssertTrue([importer conformsToProtocol:NSProtocolFromString(@"ImporterProtocol")], @"Created importer doesn't implement protocol");
    7. XCTAssertEqualObjects([importer class], [MyKMLImporter class], @"Class mismatch");
    8. }


    Was passiert sollte offensichtlich sein. Ich definiere ein Pasteboard Item mit einem Dummy-Pfad und gebe dieses Item an die Factory.
    Ich möchte eine Objektinstanz zurückbekommen (wer möchte das nicht!), die dem ImporterProtocol entspricht (damit alle Importer dieselbe Schnittstelle zum Zugriff auf ihre Daten liefern) und natürlich soll die Instanz für eine Datei .kml vom Typ MyKMLImporter sein.
    Ist sie aber nicht.

    Genau genommen lautet die Aussage:
    Test Case '-[MyImporterFactoryTests testKMLImporterCreation]' started.
    2014-08-04 19:33:21.442 MapMatcher[3808:303] Available type: public.file-url
    2014-08-04 19:33:21.442 MapMatcher[3808:303] Got file ending kml
    /Develop/mapmatcher/MapMatcher/MapMatcherTests/MyImporterFactoryTests.m:58: error: -[MyImporterFactoryTests testKMLImporterCreation] : (([importer class]) equal to ([MyKMLImporter class])) failed: ("MyKMLImporter") is not equal to ("MyKMLImporter") - Class mismatch
    Test Case '-[MyImporterFactoryTests testKMLImporterCreation]' failed (0.001 seconds).

    Ja, da steht "MyKMLImporter" != "MyKMLImporter".

    Stutzig geworden bin ich, weil der Test zuerst stumpf hieß:

    Quellcode

    1. XCTAssertTrue([importer isKindOfClass [MyKMLImporter class]], @"Class mismatch");

    und fehl schlug.

    Die einzige Änderung, die den Test jetzt scheitern lässt: das Protokoll hat eine property 'delegate' dazu bekommen, die dann entsprechend in den einzelnen spezifischen Klassen synthetisiert werden.

    Wie kann ich jetzt bitte herausfinden, warum die Klasse offenbar unterschiedlich sei bzw. wie der Test dann lauten muss um für mich sicher zu gehen, dass es sich um die gewünschte Instanz handelt?
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Ohne dass jetzt wirklich kapiert zu haben aber wenn es ohne die Delegate property geklappt hat, dann mach doch einfach eine neue Klasse die von der funktionierenden erbt und gib der dann das Delegate..

    Gruss

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Vielleicht gibt da jemand eine Metaklasse zurück, vielleicht isa-swizzelt da Cocoa irgend einen Voodoo oder XCTAssertEqualObjects erwartet Instanzen oder sowas. Dem kann man natürlich auf den Grund gehen, wenn man Spaß dran hat, aber ich würde meinen Kopf darüber nicht zerbrechen und stattdessen auf isKindOfClass: testen - das ist normalerweise aussagekräftiger.

    Edit: Oh, sorry, falsch gelesen, das hast du ja schon getestet. Das sollte funktionieren.
    Multigrad - 360°-Produktfotografie für den Mac
  • Marco Feltmann schrieb:

    Wie kann ich jetzt bitte herausfinden, warum die Klasse offenbar unterschiedlich sei bzw. wie der Test dann lauten muss um für mich sicher zu gehen, dass es sich um die gewünschte Instanz handelt?

    Sind denn die Adressen von [importer class] und [MyKMLImporter class] gleich? Höchstwahrscheinlich nicht, sonst würde der Test nicht fehlschlagen. Wie sieht denn die Methode importerForPasteboardItem: aus?
  • In Objective-C ist es kein Problem, dass eine Klasse einen anderen Namen ausgibt:

    Quellcode

    1. ​@interface Groucho : NSObject
    2. @end
    3. @implementation Groucho
    4. + (NSString *)description {
    5. return @"Harpo";
    6. }
    7. @end
    8. NSLog(@"class = %@", [Groucho class]); // -> Harpo
    Alles anzeigen
    „Meine Komplikation hatte eine Komplikation.“
  • 1. XCTAssertEqualObjects vergleicht nicht Zeiger und prüft nicht Zugehörigkeit, sondern sendet isEqual:. Da der Empfänger ein Klassenobjekt ist, müsste also die Methode +isEqual: implementiert sein. Da sie das nicht ist und die Methode bereits in einer Rootklasse implementiert ist, wenn sie denn das NSObject-Protokoll implementiert, wird -isEqual: ersatzweise ausgeführt. Keine Ahnung, was der standardmäßig macht.

    Aber das alles ist sicherlich nicht das, was du wolltest. Insofern ist es egal, was er macht.

    2. Wie bereits mattik sagte, sollte -isKindOfClass: funktionieren. Hier würde ich auch nachforschen, warum den nicht so ist. Da ich da Mocks in der Gegen herumfliegen sehe, kann es ganz einfach sein, dass -class, +class und/oder -isKindOfClass: nicht ordentlich überschrieben wurde. In Cocoa ist es nämlich so, dass eine Klasse bestimmt, als was für eine Klasse sie erscheinen soll, was bei einem anständigen Mock die gemockte Klasse sein sollte. Aber das macht nicht jeder sauber. Hier kann ich mich nur Michaels Ratschlag anschließen, sich mal die Zeiger ausgeben zu lassen.

    Ansonsten verweise ich auf die Mitschriften von heise-Redakteuren zu meinen Vorträgen dazu.

    3. Mutmaßlich willst du dort ohnehin keine Vererbung berücksichtigen, dann reicht ganz einfach == auf die Klassenobjektreferenzen. Klassenobjekte sind garantiert und dokumentiert Singletons. Aber auch hier 2.

    Ach, so: Wenn 2 fehlschlagen sollte, weil -class, +class und/oder -isKindOfClass: nicht ordentlich in einem Mock implementiert sind, wird es sicherlich ersatzweise so etwas wie +mockedClass geben. Das sollte dann funktionieren. Wenn es das nicht gibt, kann man das ja selbst einbauen.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Amin Negm-Awad ()

  • Der/die/das Mock hat damit überhaupt nichts zu tun.
    Zum einen ist es kein Mock, sondern ein billiges NSPasteBoardItem, das hardcoded absolut sinnlos ist und ausschließlich die Werte zurück gibt, die ich ihm vorgebe.
    Zum anderen wird es lediglich an den Convenience Allocator der Factory übergeben und spielt bei den späteren Vergleichen keine Rolle mehr.

    Es kommt eine definitive Klasse MyKMLImporter dabei heraus.

    Die Zeiger zeigen Unterschiede:
    Instance class: MyMKMLImporter, Class class: MyMKMLImporter
    Instance class: 0x10001d348, Class class: 0x100263ad8


    Da sie das beim XMLImporter, dem GPXImporter und dem PlistImporter nicht tun, muss da also irgendwas anders laufen als von mir geplant. +seufz+

    Wie dem auch sei. Für die Funktionalität ist es ja wurscht, ob die Klasse jetzt stimmt oder nicht. Für die einzelnen Importer gibt es ja noch eigene Tests.
    Ich prüfe also hier einfach auf den Namen der Klassen und wenn die stimmen, dann reicht mir das an Testgenauigkeit.
    Wenn das Ding behauptet, MyKMLImporter zu sein, und ich die Gewissheit habe, dass mein Importer Protokoll erfüllt wird, dann ist das erst mal okay.

    Verstehen verstehe ich es dennoch nicht. ;)
    Denn "gestern ging's noch".
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Marco Feltmann ()

  • Äh, wenn -isMemberOfClass: YES liefert, sollte es -isKindOfClass: auch tun. Wenn also -isKindOfClass: fehlerhaft NO liefert (also YES liefern müsste), dürfte -isMemberOfClass: nicht YES liefern.

    Irgendwie ist da was ganz kaputt.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Sollte…
    - (BOOL)isMemberOfClass:(Class)aClass
    Class objects may be compiler-created objects but they still support the concept of membership. Thus, you can use this method to verify that the receiver is a specific Class object.


    - (BOOL)isKindOfClass:(Class)aClass
    If the receiver is a class object, this method returns YES if aClass is a Class object of the same type, NO otherwise.


    Zumindest beim Senden der Nachricht an ein Klassenobjekt gibt es ein anderes Verhalten bei isKindOfClass: als beim Senden der Nachricht an ein Instanzobjekt.
    Was durchaus logisch ist, da ein Klassenobjekt in den seltensten Fällen eine Subklasse eines bestimmten Klassenobjekts sein dürfte.

    Unter welchen Umständen -isMemberOfClass: ein YES zurückliefert, -isKindOfClass: hingegen ein NO, ist mir dennoch schleierhaft.
    Da aber -isMemberOfClass: und -isKindOfClass: unterschiedlich implementiert sind und -isKindOfClass: nicht notwendigerweise -isMemberOfClass: aufrufen muss, kann da ein abweichendes Verhalten durchaus vorkommen.

    Wenn ich mal zu viel Zeit habe versuche ich ein Testprojekt zu dem Thema aufzusetzen.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Super, da kommt man auf die Idee ein Testprojekt zu bauen und 'plötzlich' geht -isMemberOfClass: auch nicht mehr.
    Immerhin verhält es sich jetzt so, wie man es erwarten würde…
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Marco Feltmann schrieb:

    Sollte…
    - (BOOL)isMemberOfClass:(Class)aClass
    Class objects may be compiler-created objects but they still support the concept of membership. Thus, you can use this method to verify that the receiver is a specific Class object.


    - (BOOL)isKindOfClass:(Class)aClass
    If the receiver is a class object, this method returns YES if aClass is a Class object of the same type, NO otherwise.


    Zumindest beim Senden der Nachricht an ein Klassenobjekt gibt es ein anderes Verhalten bei isKindOfClass: als beim Senden der Nachricht an ein Instanzobjekt.
    Was durchaus logisch ist, da ein Klassenobjekt in den seltensten Fällen eine Subklasse eines bestimmten Klassenobjekts sein dürfte.

    Unter welchen Umständen -isMemberOfClass: ein YES zurückliefert, -isKindOfClass: hingegen ein NO, ist mir dennoch schleierhaft.
    Da aber -isMemberOfClass: und -isKindOfClass: unterschiedlich implementiert sind und -isKindOfClass: nicht notwendigerweise -isMemberOfClass: aufrufen muss, kann da ein abweichendes Verhalten durchaus vorkommen.

    Wenn ich mal zu viel Zeit habe versuche ich ein Testprojekt zu dem Thema aufzusetzen.
    Ich verstehe nicht, was das mit meinem Beitrag zu tun hat. Ich habe nicht gesagt, dass es sich bei Klassenobjekten genau so verhält wie bei Instanzobjekten. (Um Klassenobjekte habe ich mich gar nicht gekümmert.) Ich habe gesagt, dass wenn isMemberOfClass: ein YES liefert, dies -isKindOfClass: auch tun sollte.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Marco Feltmann schrieb:

    Super, da kommt man auf die Idee ein Testprojekt zu bauen und 'plötzlich' geht -isMemberOfClass: auch nicht mehr.
    Immerhin verhält es sich jetzt so, wie man es erwarten würde…
    Klar, weil in deinem "Produktivprojekt" etwas komisch ist. Hatte ich aber schon gesagt.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Amin Negm-Awad schrieb:

    Marco Feltmann schrieb:

    Super, da kommt man auf die Idee ein Testprojekt zu bauen und 'plötzlich' geht -isMemberOfClass: auch nicht mehr.
    Immerhin verhält es sich jetzt so, wie man es erwarten würde…
    Klar, weil in deinem "Produktivprojekt" etwas komisch ist. Hatte ich aber schon gesagt.

    Ich fürchte, Du hast Dich geirrt.
    Im 'UnitTest Projekt' war etwas komisch.

    Wenn man vergisst, das Häkchen vor einem Target für das Objekt zu setzen, so erinnerte ich mich, dass sich Dinge nicht kompilieren ließen.
    Wenn man im UnitTest Target das das Häkchen für das Objekt nicht setzt, so kompiliert es und kommt offenbar bei dem ganzen -isKindOf: und -isMemberOf: irgendwie durcheinander. Manchmal. Warum auch immer.

    Fun Fact: setzt man das Häkchen sowohl für das App Target als auch für das Test Target, so ist gemäß Logausgabe auch nicht garantiert, dass es läuft.
    objc[7313]: Class MyImporterFactory is implemented in both /Users/feltmann/Library/Developer/Xcode/DerivedData/App-gvvzqxhtfvygqfgquhnceihcsplw/Build/Products/Debug/Target.app/Contents/MacOS/Target and /Users/feltmann/Library/Developer/Xcode/DerivedData/App-gvvzqxhtfvygqfgquhnceihcsplw/Build/Products/Debug/TargetTests.xctest/Contents/MacOS/TargetTests. One of the two will be used. Which one is undefined.


    Zur Not werde ich mich erst einmal darauf hoffen müssen, dass der sich schon das richtige Objekt da raus greift.
    Falls nicht, muss ich für jedes Geteste das App-Target vor den Implementierungen entfernen, damit sie ausschließlich und hoheitlich dem Test Target zur Verfügung stehen.
    (Was im Übrigen meinem Verständnis von Unit Tests entgegen läuft.)

    Falls jemand eine andere/bessere Idee hat bin ich ganz Ohr. :)
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Marco Feltmann schrieb:


    Wenn man vergisst, das Häkchen vor einem Target für das Objekt zu setzen, so erinnerte ich mich, dass sich Dinge nicht kompilieren ließen.


    Kompilieren oder Linken?

    Marco Feltmann schrieb:


    Wenn man im UnitTest Target das das Häkchen für das Objekt nicht setzt, so kompiliert es und kommt offenbar bei dem ganzen -isKindOf: und -isMemberOf: irgendwie durcheinander. Manchmal. Warum auch immer.


    Hast du eigentlich irgendwo ein Testprojekt hochgeladen?

    Marco Feltmann schrieb:


    Fun Fact: setzt man das Häkchen sowohl für das App Target als auch für das Test Target, so ist gemäß Logausgabe auch nicht garantiert, dass es läuft.


    Da es der selbe Code zwei Mal ist, läuft es. Sollte aber trotzdem nicht sein.
  • 1) Kompilieren
    2) Nope, beim Erstellen eines Testprojektes trat der Fehler nicht mehr auf. (Hab das mit dem Häkchen eben erst gesehen – und warum ein Testprojekt zu einem Fehler erstellen, den man behoben hat?)
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P