Singleton + "globale Variable" - Laufzeitfehler

  • Singleton + "globale Variable" - Laufzeitfehler

    Hallo,

    folgendes habe ich brav gelesen:
    - osxentwicklerforum.de/thread.p…adid=2371&hilight=globale
    - macentwicklerwelt.net/index.php?title=Singleton

    Und versucht in mein Programm zu integrieren.

    Vorhanden ist eine Singletonklasse, welche eine Datenbankverbindung repräsentiert. Beim Verwenden der Singletonklasse erhalte ich einen Laufzeitfehler.

    Der Source:

    DatabaseConnection.h:

    Quellcode

    1. #import <Cocoa/Cocoa.h>
    2. #import <QuickLite/QuickLiteDatabase.h>
    3. @interface DatabaseConnection : NSObject {
    4. QuickLiteDatabase *_databaseConnection;
    5. }
    6. - (QuickLiteDatabase *)databaseConnection;
    7. @end
    Alles anzeigen


    DatabaseConection.m:

    Quellcode

    1. #import "DatabaseConnection.h"
    2. @implementation DatabaseConnection
    3. + (DatabaseConnection *)_instance {
    4. static DatabaseConnection* databaseConnection = nil;
    5. if (databaseConnection == nil) {
    6. databaseConnection = [QuickLiteDatabase databaseWithFile:[[NSBundle mainBundle] pathForResource:@"data" ofType:@"sql"]];
    7. }
    8. return databaseConnection;
    9. }
    10. - (QuickLiteDatabase *)_databaseConnection {
    11. // if([_databaseConnection open] == NO)
    12. // {
    13. // NSLog(@"Error in: \"- (QuickLiteDatabase *)_databaseConnection\". Message: Could not open database connection.");
    14. // }
    15. return _databaseConnection;
    16. }
    17. + (QuickLiteDatabase *)databaseConnection {
    18. return [[self _instance] _databaseConnection];
    19. }
    20. @end
    Alles anzeigen


    Der Fehler:

    Quellcode

    1. -[QuickLiteDatabase _databaseConnection]: selector not recognized [self = 0x342b60]
    2. An uncaught exception was raised
    3. -[QuickLiteDatabase _databaseConnection]: selector not recognized [self = 0x342b60]
    4. Uncaught exception: <NSInvalidArgumentException> *** -[QuickLiteDatabase _databaseConnection]: selector not recognized [self = 0x342b60]


    Ich finde den Fehler nicht. Hilfe :)
    Die Objective-Cloud ist fertig wenn sie fertig ist. Beta heißt Beta.

    Objective-C und Cocoa Band 2: Fortgeschrittene
    Cocoa/Objective-C Seminare von [co coa:ding].
  • RE: Singleton + "globale Variable" - Laufzeitfehler

    Original von Objcler
    Ich finde den Fehler nicht. Hilfe :)

    Naja, Du hast auch Dein Möglichstes getan, um den Code verwirrend zu machen :) Du hast eine statische Variable databaseConnection, eine Instanzvariable _databaseConnection, eine Klassenmethode databaseConnection und eine Instanzmethode _databaseConnection - und einige stehen für die Klasse QuickLiteDatabase und andere für DatabaseConnection. Ganz schön viele unterschiedliche Dinge für einen einzigen Namen - da kann man schon bei ca. 10 Zeilen nichts mehr finden.

    Das Problem liegt (soweit ich das sehen kann) in der Klassenmethode +_instance. Die soll eigentlich eine Instanz der Klasse DatabaseConnection zurückgeben, sie erzeugt allerdings eine Instanz der Klasse QuickLiteDatabase. Deine Klassenmethode +databaseConnection will sich das DatabaseConnection-Singleton holen und darin die Methode -_databaseConnection aufrufen. Doch leider bekommt es kein DatabaseConnection-Objekt (wie die Signatur von _instance andeutet), sondern ein QuickLiteDatabase-Objekt, das die Methode -_databaseConnection nicht kennt.

    Ich würde Dir gaaaanz dringend empfehlen, bezeichnendere Namen einzuführen - dann verläuft man sich darin nicht so.

    Ach ja: Obwohl die Unsitte mit den Underscores ("_") vor privaten Eigenschaften und Methoden weit verbreitet ist (auch hier im Forum), ist sie nicht richtig. Anscheinend hat sich das irgendwer mal aus den Apple-Headern abgeschaut, ohne in der Doku zu lesen, dass sie Entwicklern explizit davon abraten.
    Multigrad - 360°-Produktfotografie für den Mac
  • Hi mattik,

    danke für deine Hinweise... Habe es mittlerweile aber selbst gelöst :) Ich stand echt auf dem Schlauch... die Lösung war ja ganz simpel...

    Also künftig immer statt _foo - foo schreiben? Okay :)

    Quellcode

    1. #import <Cocoa/Cocoa.h>
    2. #import <QuickLite/QuickLiteDatabase.h>
    3. @interface DatabaseConnection : NSObject {
    4. QuickLiteDatabase *_databaseConnection;
    5. }
    6. + (QuickLiteDatabase *)databaseConnection;
    7. @end
    Alles anzeigen


    Quellcode

    1. #import "DatabaseConnection.h"
    2. @implementation DatabaseConnection
    3. + (DatabaseConnection *)_instance {
    4. static DatabaseConnection* singletonObject = nil;
    5. if (singletonObject == nil) {
    6. singletonObject = [[DatabaseConnection alloc] init];
    7. }
    8. return singletonObject;
    9. }
    10. - (QuickLiteDatabase *)_databaseConnection {
    11. if(_databaseConnection == nil) {
    12. _databaseConnection = [[QuickLiteDatabase alloc] initWithFile:[[NSBundle mainBundle] pathForResource:@"data" ofType:@"sql"]];
    13. if([_databaseConnection open] == YES) {
    14. NSLog(@"connection established");
    15. }
    16. else {
    17. NSLog(@"connection failed");
    18. }
    19. }
    20. else {
    21. NSLog(@"not nil");
    22. }
    23. return _databaseConnection;
    24. }
    25. + (QuickLiteDatabase *)databaseConnection {
    26. return [[self _instance] _databaseConnection];
    27. }
    28. @end
    Alles anzeigen
    Die Objective-Cloud ist fertig wenn sie fertig ist. Beta heißt Beta.

    Objective-C und Cocoa Band 2: Fortgeschrittene
    Cocoa/Objective-C Seminare von [co coa:ding].
  • RE: Singleton + "globale Variable" - Laufzeitfehler

    Ich habe zwar auch nicht den Link parat, kann das aber bestätigen:
    Apple selbst sagt, dass nur Apple _-Bezeichner verwenden darf. Aber es hat sich eben, wie mattik ebenfalls ganz richtig sagt, so eingebürgert.

    Ohne Unterzug ist es auch einfach unpraktisch. Denke nur an die Fällle, in denen du etwa ein Array aufbaust und dann setzt. Da hättest du ne verdeckte Variable. Man kann dann natürlich auf newArray ausweichen.
    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"?
  • RE: Singleton + "globale Variable" - Laufzeitfehler

    Original von læng
    @mattik
    Hast du mal nen Doku-Link zum Unterstrich-Thema?

    Du bekommst sogar zwei.

    Google: "cocoa underscore site:developer.apple.com"

    1. Hit: Coding Guidelines for Cocoa: Naming Methods

    Don’t use the underscore character as a prefix for your private methods. Apple reserves this convention.

    3. Hit: Codding Guidelines for Cocoa: Code Naming Basics

    Avoid the use of the underscore character as a prefix meaning private, especially in methods. Apple reserves the use of this convention. [...]

    Finde ich ziemlich eindeutig...
    Multigrad - 360°-Produktfotografie für den Mac
  • RE: Singleton + "globale Variable" - Laufzeitfehler

    Eine verdeckte Variable ist nicht eine gleichlautende Variable in der Subklasse (Das ist der Grund für Apples Empfehlung), sondern eine lokale Variable, die wie eine Instanzvariable heißt:

    Quellcode

    1. interface ...
    2. NSArray* threads;
    3. ...
    4. }
    5. ...
    6. @end

    Quellcode

    1. - (void)rearrangeThreads {
    2. ....
    3. threads = [NSMutableArray array];
    4. ...
    Da bekommst du nämlich eine Fehlermeldung, sinngemäß zitiert:
    local variable threads hides intance variable
    .Dieses Problem vermeidest du, indem du den Instanzvariablen Unterzüge gibst, allso gegen Apples Empfehlung handelst. Natürlich kann man auch ganz einfach so etwas wie newThreads benutzen, wie ich bereits schrieb.
    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"?
  • RE: Singleton + "globale Variable" - Laufzeitfehler

    Original von Tom9811
    Eine verdeckte Variable ist nicht eine gleichlautende Variable in der Subklasse (Das ist der Grund für Apples Empfehlung), sondern eine lokale Variable, die wie eine Instanzvariable heißt:

    Ob nun verdeckte Variable oder gleichlautende Variable, in beiden Fällen kommt es zur Kollision mit der Superklasse. Das nur die gleich lautende Variable der Grund für Apples Empfehlung ist kann ich jetzt der Dokumentation auf die Schnelle nicht entnehmen.

    Michael
  • RE: Singleton + "globale Variable" - Laufzeitfehler

    Bei dem einen droht die Gefahr, wenn du ein Unterzugs hinzufügst (Subklass), bei dem anderen, wenn du es weglässt (verdeckte Variable).

    Da Apple sagt, man solle es weglassen, kann kaum die zweite Variante Motivation gewesen sein.
    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"?
  • RE: Singleton + "globale Variable" - Laufzeitfehler

    Also, wenn Du durch Hinzufügen eines Unterzugs den gleichen Namen erhältst, wie Apple ihn zufälligerweise gerade in der Superklasse als eine interne Membervariable verwendet, dann versteckst Du die Apple-Variable. Darum reserviert Apple die Konvention mit dem Unterstrich für sich. Übrigens gibt "Verstecken" nur eine Warnung, ein "double member" gibt einen Fehler, d.h. Du bekommst das gar nicht erst compiliert. Daher dürfte gerade die "Verstecken" Variante eher die Motivation sein.

    Michael
  • RE: Singleton + "globale Variable" - Laufzeitfehler

    Richtig, es gibt einen Fehler. Deshalb ist es ja auch kein Verstecken.

    Aber ich verstehe dich dennoch nicht:

    Ich sagte, dass versteckte Variablen, also diejenigen, die der Compiler mit "hide" bezeichnet, entstehen können, wenn man den gleichen Namen verwendet und eine Instanz ohne Unterzug hat. Da man dieses Problem mit einem Unterzug umgehen kann, Apple aber sagt, dass man keinen Unterzug verwenden soll, können sie kaum dieses Problem im Kopf gehabt haben.
    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"?
  • RE: Singleton + "globale Variable" - Laufzeitfehler

    Ich glaube, dass Apple primär gar nicht die Variablen im Sinn hatte, sondern Methoden. Bei Konflikten von Variablennamen (seien es nun doppelte Instanzvariablen oder Kollisionen zwischen Instanzvariablen und lokalen Variablen oder Parametern) gibt's i.d.R. einen Fehler oder eine Warnung. Und es gibt ein syntaktische Konstrukte, um Instanzvariablen vor dem Zugriff von außen bzw. von Unterklassen zu vermeiden (eigenartigerweise scheint Apple wenig Gebrauch von @private zu machen, aber das ist ein anderes Thema).

    Bei Methoden sieht es anders aus, weil die bei ObjC prinzipbedingt hier gnadenlos offen und dynamisch ist. Der Compiler hat keine Chance, zu warnen, weil er gewolltes nicht von versehentlichem Überschreiben von Methoden unterscheiden kann. Eine Namespace-Kollision einer privaten Methode eines Entwicklers mit einer privaten von Apple legt interne Mechanismen der Basisklasse lahm, was mit ziemlicher Sicherheit zum Zusammenbruch führt. Der Anwender hat keine Möglichkeit, das zu verhindern, weil er normalerweise nciht weiß, welche Methodennamen Apple schon verwendet hat und der Compiler auch keinen Mucks sagt - es funktioniert schlicht nicht. Deshalb die Konvention - das "Verbot" sehe ich eher so: Apple ist so freundlich, alle privaten Methoden mit einem Unterzug zu beginnen, damit alle anderen Anwender den gesamten restlichen Namespace zu ihrer freien Verfügung haben.

    Einen Grund für Variablennamen mit Unterzug finde ich nachvollziehbar: Knappheit im Namespace. Logischerweise hat man oft einen Parameter oder eine lokale Variable, dessen Bedeutung einer Instanzvariable nahe kommt (z.B., wie Tom sagt, um eine Instanzvariable vorzubereiten, zu ersetzen o.ä.). Andererseits finde ich den Underscore hier keine sonderlich schöne Lösung, denn erstens sagt er nicht viel aus (sondern ist nur ein Ausweg um zwei ähnliche Variablennamen zu bekommen), zweitens steht der Instanzvariablen der unveränderte Name zu (finde ich zumindest - schließlich hat sie normalerweise den größeren Scope) und drittens hat das nur selten mit private/public im engeren Sinne zu tun (zumindest ich verwende so gut wie nie öffentliche Instanzvariablen, sondern Accessoren, sodass ich nie davon ausgehe, das Instanzvariablen öffentlich sind - in den wenigen Ausnahmen ist sowieso ein ausführlicher begründender Kommentar und/oder eine besondere Benennung der Eigenschaft sinnvoll).
    Multigrad - 360°-Produktfotografie für den Mac
  • RE: Singleton + "globale Variable" - Laufzeitfehler

    Die andere Möglichkeit besteht natür lich daran, dass der Apple-Ratschlag erst zum Problem verdeckter Variablen führt und sich die Ansicht, dies sei der Grund für den Ratschlag, dann irgendwie ziemlich sinnfrei ist.

    Man wird es wohl nie erfahren.
    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"?
  • RE: Singleton + "globale Variable" - Laufzeitfehler

    Na, wenn du einen Instanzvariablenbezeichner mit Underscore nimmst, also gegen Apples Ratschlag, dann bist du zwar die Problematik cerdeckter Variablen los. Aber du hast schon Recht, dass es da schönere Lösungen gibt, wie etwa newX oder Ähnliches.

    Das Problem dürfte vor allem darin liegen, dass Apple ja nachträgllich Objekte erweitern kann. Und dann ist es bei Verweundung von Unterzügen jedenfalls denkbar, dass es zu einem Konflikt kommt. Gerade dann, wenn man bei den Bezeichnungen nicht sonderliche Phantasie walten lässt, lässt sich die Source möglicherweise später nicht mehr compilieren. Man sollte isch schon an Apples Ratschlag halten, auch wenn ich es mmir selbst anders angewöhnt habe.

    Wäre wirklich mal interessant herauszufinden, wer damit angefangen hat.
    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"?
  • RE: Singleton + "globale Variable" - Laufzeitfehler

    Original von Tom9811
    Das Problem dürfte vor allem darin liegen, dass Apple ja nachträgllich Objekte erweitern kann.

    Ich hab's nicht ausprobiert, aber ich glaube nicht, dass das geht. Wenn sich die Länge der Instanzvariablen ändert, dürfte zumindest eine Neukompilierung angesagt sein. Die Instanzvariablen werden nicht so dynamisch gehandelt wie die Methoden, sondern die Offsets zur Compile Time verdrahtet (zumindest teilweise - ich habe mir den Mechanismus nie im Ganzen und im Detail angeschaut). Selbst wenn private Instanzvariablen von Apple und eines anderen Entwicklers den gleichen Namen hätten, könnte es ohne Kollision klappen - z.B. wenn Apple ihre Variablendeklaration im öffentlichen Header durch ein "_reserved[]" o.ä. obfuskiert (Igitt! ein Java-Wort!), sollten eigentlich beide Teile unbehelligt voneinander weiterlaufen. Neenee, ich glaube immer noch, dass die ursprüngliche Motivation die Methoden waren und die Regel dann aus Konsistenzgründen und zur Sicherheit verallgemeinert wurde. Kannst Du nicht einfach NSKristallkugel fragen?
    Multigrad - 360°-Produktfotografie für den Mac