Objective C: Macht man sowas

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

  • Objective C: Macht man sowas

    Hi,

    das "Problem": Ich lade z.B. in einer Methode Daten. Ich setze den Networkindicator auf visible.

    Nun gibt es mehrere Stellen wo abgebrochen werden muss. Wenn die geladenen Daten nil sind, wenn die deserialisierung in die Hose geht...

    Jedes mal, bevor ich mit return raus gehe, muss ich dran denken, den Networkindicator wieder auszuschalten.

    In einem Kurs den ich bei Java Leuten gehalten habe, wurde gefragt, warum man das nicht mit @try @finally macht. Ist wohl in Java üblich.

    In ObjC nicht, jedenfalls nicht das ich wüsste. Aber dumm ist es irgendwie auch nicht. So wird nicht aus versehen vergessen den Networkindicator auszuschalten, oder was es auch immer so an abschließenden Arbeiten gibt, die man auf alle Fälle machen muss.

    Quellcode

    1. - (void) loadData
    2. {
    3. [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: YES];
    4. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    5. dispatch_async(queue, ^{
    6. @try
    7. {
    8. NSURL *url = [NSURL URLWithString: @"https://itunes.apple.com/lookup?id=396306964&entity=software&country=de"];
    9. NSData *data = [[NSData alloc] initWithContentsOfURL: url ];
    10. if (data == nil)
    11. {
    12. [[[NSException alloc] initWithName: @"LoadError" reason: @"DerGrund" userInfo: nil] raise]; //ARC ist manchmal komisch :-)
    13. }
    14. NSError *error = nil;
    15. NSDictionary *unserialize = [NSJSONSerialization JSONObjectWithData: data
    16. options: 0
    17. error: &error];
    18. if (error != nil)
    19. {
    20. [[[NSException alloc] initWithName: @"DesrializationError" reason: @"DerGrund" userInfo: nil] raise];
    21. }
    22. NSArray *results = [unserialize objectForKey: @"results"];
    23. if (results == nil)
    24. {
    25. [[[NSException alloc] initWithName: @"FormatError" reason: @"DerGrund" userInfo: nil] raise];
    26. }
    27. _data = results;
    28. }
    29. @catch (NSException *exception)
    30. {
    31. // vielleicht schauen ob es unsere ist
    32. NSLog(@"%@", exception);
    33. _data = @[]; // wenns schief geht z.B. auf alle fälle ein leeres Array
    34. }
    35. @finally
    36. {
    37. dispatch_async(dispatch_get_main_queue(), ^{
    38. [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: NO];
    39. [[self tableView] reloadData];
    40. });
    41. }
    42. });
    43. }
    Alles anzeigen


    Was meint ihr - oder bin ich etwa wieder der Einzige der das nicht so macht ;)

    Gruß
    Manfred
    Seminare, Artikel, Code. ObjectiveCeeds - alles für die Apfelzucht.
  • Es geht jetzt nicht darum, kreative Ideen zu sammeln wie man das jetzt in dem Fall anders machen könnte.

    Wir werten eine Kette von Fehlern/Abbruchbedingungen aus. Bei jedem Abbruch haben wir Aufräumarbeiten die immer gleich sind.

    Ich kann den Networkindicator auch nach NSData *data = [[NSData alloc] initWithContentsOfURL: url ]; einfach ausschalten und gut ist, da brauch ich keinen sendAsynchronousRequest:queue:completionHandler: dafür.


    Aber den Programmablauf quasi über Exceptions zu steuern ist irgendwie ungewohnt. Für mich ist das immer ehr die letzte Möglichkeit gewesen dem Anwender meiner Klasse einen Schuss vor den Bug zu geben.
    Seminare, Artikel, Code. ObjectiveCeeds - alles für die Apfelzucht.
  • wenns schon synchron ist dann würd ich mir den einen teil in eine eigenen methode packen. ist ja auch wiederverwenbar.
    also laden, entpacken deserialisieren, überprüfen und was weiß ich noch alles in eine einzige methode die nach dem anzeigen des indikators aufgerufen wird. sobald die methode durch ist, blendet man ihn wieder aus.
    solcher code ist ja auch meist wiederverwendbar weil man sowas öfter brauchen kann und sich dann schnell per c&p holen kann ohne den ganzen müll bezüglich GUI etc mit drin zu haben ;)
  • Ich finde den Code ziemlich umständlich. Ausnahmen sind ja eher dafür gedacht, über mehrere Aufrufebenen zu einer Fehlerbehandlung zurückzuspringen, und dabei in der Regel den Code leserlicher zu machen. In Deinem Beispiel bringen sie da keine Vorteile. Da würde ich den Kram im @try-Block in eine Methode packen, die entweder das Ergebnis oder einen Fehler liefert, und @try-@catch-@finally durch ein if-else ersetzen.

    BTW: Anstelle von objectForKey: eignet sich an dieser Stelle valueForKey:; da sparst Du Dir ein if... ;)
    „Meine Komplikation hatte eine Komplikation.“
  • Ich persönlich (Java ist Schuld...) mag Exceptions mittlerweile ein wenig lieber als noch vor einem Jahr.
    Sie sind prima, um bei gravierenden Problemen eben nicht allen Kleinkram händisch lösen zu müssen, sondern über den @finally Block trotzdem wichtige Aufgaben ausführen zu können.

    Insofern spricht aus meiner Sicht nichts gegen diesen Einsatz.

    Allerdings finde ich den Ansatz doch eher kontraproduktiv. Exceptions sollen dafür sorgen, dass das Programm ohne Schaden anzurichten beendet wird. Sie abzufangen halte ich für den falschen Weg, weil dann mit einer inkonsistenten Datenstruktur gearbeitet wird.

    Idee: (IDE = Safari, also eher mittelmäßig)

    C-Quellcode

    1. - (void) loadData
    2. {
    3. [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: YES];
    4. _data = [self arrayFromURL:[NSURL URLWithString: @"https://itunes.apple.com/lookup?id=396306964&entity=software&country=de"]];
    5. dispatch_async(dispatch_get_main_queue(), ^{
    6. [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: NO];
    7. [[self tableView] reloadData];
    8. });
    9. }
    10. - (NSArray*)arrayFromURL:(NSURL*)theURL {
    11. NSDictionary * unserialized = [self unserializedDictionaryFromURL:theURL];
    12. NSArray * result = [unserialized objectForKey:@"results"];
    13. if(result != nil) {
    14. result = [NSArray array];
    15. }
    16. return result;
    17. }
    18. - (NSDictionary*)unserializedDictionaryFromURL:(NSURL*)theURL {
    19. NSData * dataFromURL = [self dataFromURL:theURL];
    20. NSError *error = nil;
    21. NSDictionary *unserialize = [NSJSONSerialization JSONObjectWithData:dataFromURL options: 0 error: &error];
    22. if(error) {
    23. unserialize = [NSDictionary dictionary];
    24. }
    25. return unserialize;
    26. }
    27. - (NSData*)dataFromURL:(NSURL*)theURL {
    28. NSData *data = [[NSData alloc] initWithContentsOfURL:theURL];
    29. if(data == nil) {
    30. data = [[NSData alloc] init];
    31. }
    32. return data;
    33. }
    Alles anzeigen

    Damit sollten Probleme mit nil ausfallen und egal was passiert, das Rädchen hört auf zu drehen.

    Die Frage ist ja immer, was ein unerwarteter Ausnahmefehler ist.
    Das NSURL Argument könnte nil sein, was definitiv ein Fehler des Programmierers ist. (Gibt es eine Möglichkeit zu verhindern, dass nil übergeben wird, oder muss ich immer manuell eine Exception werfen?)
    Es ist also ein schwerer Ausnahmefehler und muss dem Programmierer Kund getan werden. Und in dem Fall (also eines schweren Ausnahmefehlers) ist es auch völlig wurscht, ob das Rad aufhört zu drehen oder nicht – die App beendet sich.

    Die Seite kann nicht erreicht werden? Das halte ich für keinen schweren Ausnahmefehler. Eher für ein wahrscheinliches Szenario. WLAN weg, Internet aus, Server down...
    Da sollte die App nicht mit einer Exception abschmieren sondern nur einen Infodialog anzeigen (und dann halt die alten Daten zeigen.)

    Die Daten konnten nicht geparst werden? Hat der User eigentlich auch nix mit zu tun. Sagen kann man es ihm ja, aber abschmieren muss es deswegen ja noch lange nicht – er kann ja vermutlich eh nix daran ändern.

    Die Daten sind einfach leer? Kann passieren. Das Script war nicht fertig, URL stimmt nicht, irgendwer hat rumgebastelt...
    Auch das ist kein Grund dafür, die App abschmieren zu lassen.

    Also ich würde es immer durchlaufen lassen und gemäß den Error Handling Guides von Apple eher ein NSError Objekt mitschleppen.
    (Dann natürlich im Rahmen der Codehygiene einfach IMMER das Error-Objekt behandeln. Wenn's nil ist passiert einfach nix.)
    «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
  • Exceptions sind in Objective-C/Cocoa kontrakonzeptionell. Sie sind /niemals/ dafür gedacht, einen vorhergesehenen Kontrollfluss zu steuern. Daher stellt sich die Frage nicht. Übrigens neigt Java-Code dazu, so viele Exceptions zu fangen, dass es am Ende eine try-Wüste wird. Charmant sieht das nur im Einzelfall aus. Was ist eigentlich, wenn du es vergisst, eine Exception zu werfen?

    Soooo doof ist ein goto in einem solchen Falle gar nicht. Eine Exception ist schließlich auch ein goto, nur ein goto, dass sich an gar keine Struktur mehr hält, sozusagen das MEGAGOTO!!!!!!!!!

    Aber du hast ein überschaubares Stück Code, das sich überschaubar debuggen lässt. Wenn du vergisst den Indicator wieder auszuschalten, dann gibt es eine ganz einfache Technik: Bug-Fixing. Es ist tatsächlich so, dass man nicht Code so strukturieren kann, dass er garantiert fehlerfrei ist. Und derlei Versuche führen in der Regel auch nur zu Code, der dermaßen kompliziert ist, dass er garantiert nicht fehlerfrei ist. Habe ich gerade erst in einem Projekt beobachten dürfen. Und Stoßtrupp hat mit dem Konzept "Möglichst so kompliziert, dass es keiner mehr beherrscht" eine ganze Sprache entwickelt.
    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:

    Ich persönlich (Java ist Schuld...) mag Exceptions mittlerweile ein wenig lieber als noch vor einem Jahr.
    Sie sind prima, um bei gravierenden Problemen eben nicht allen Kleinkram händisch lösen zu müssen, sondern über den @finally Block trotzdem wichtige Aufgaben ausführen zu können.

    Insofern spricht aus meiner Sicht nichts gegen diesen Einsatz.

    Allerdings finde ich den Ansatz doch eher kontraproduktiv. Exceptions sollen dafür sorgen, dass das Programm ohne Schaden anzurichten beendet wird. Sie abzufangen halte ich für den falschen Weg, weil dann mit einer inkonsistenten Datenstruktur gearbeitet wird.

    Idee: (IDE = Safari, also eher mittelmäßig)

    C-Quellcode

    1. - (void) loadData
    2. {
    3. [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: YES];
    4. _data = [self arrayFromURL:[NSURL URLWithString: @"https://itunes.apple.com/lookup?id=396306964&entity=software&country=de"]];
    5. dispatch_async(dispatch_get_main_queue(), ^{
    6. [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: NO];
    7. [[self tableView] reloadData];
    8. });
    9. }
    10. - (NSArray*)arrayFromURL:(NSURL*)theURL {
    11. NSDictionary * unserialized = [self unserializedDictionaryFromURL:theURL];
    12. NSArray * result = [unserialized objectForKey:@"results"];
    13. if(result != nil) {
    14. result = [NSArray array];
    15. }
    16. return result;
    17. }
    18. - (NSDictionary*)unserializedDictionaryFromURL:(NSURL*)theURL {
    19. NSData * dataFromURL = [self dataFromURL:theURL];
    20. NSError *error = nil;
    21. NSDictionary *unserialize = [NSJSONSerialization JSONObjectWithData:dataFromURL options: 0 error: &error];
    22. if(error) {
    23. unserialize = [NSDictionary dictionary];
    24. }
    25. return unserialize;
    26. }
    27. - (NSData*)dataFromURL:(NSURL*)theURL {
    28. NSData *data = [[NSData alloc] initWithContentsOfURL:theURL];
    29. if(data == nil) {
    30. data = [[NSData alloc] init];
    31. }
    32. return data;
    33. }
    Alles anzeigen

    Damit sollten Probleme mit nil ausfallen und egal was passiert, das Rädchen hört auf zu drehen.

    Die Frage ist ja immer, was ein unerwarteter Ausnahmefehler ist.
    Das NSURL Argument könnte nil sein, was definitiv ein Fehler des Programmierers ist. (Gibt es eine Möglichkeit zu verhindern, dass nil übergeben wird, oder muss ich immer manuell eine Exception werfen?)
    Es ist also ein schwerer Ausnahmefehler und muss dem Programmierer Kund getan werden. Und in dem Fall (also eines schweren Ausnahmefehlers) ist es auch völlig wurscht, ob das Rad aufhört zu drehen oder nicht – die App beendet sich.

    Die Seite kann nicht erreicht werden? Das halte ich für keinen schweren Ausnahmefehler. Eher für ein wahrscheinliches Szenario. WLAN weg, Internet aus, Server down...
    Da sollte die App nicht mit einer Exception abschmieren sondern nur einen Infodialog anzeigen (und dann halt die alten Daten zeigen.)

    Die Daten konnten nicht geparst werden? Hat der User eigentlich auch nix mit zu tun. Sagen kann man es ihm ja, aber abschmieren muss es deswegen ja noch lange nicht – er kann ja vermutlich eh nix daran ändern.

    Die Daten sind einfach leer? Kann passieren. Das Script war nicht fertig, URL stimmt nicht, irgendwer hat rumgebastelt...
    Auch das ist kein Grund dafür, die App abschmieren zu lassen.

    Also ich würde es immer durchlaufen lassen und gemäß den Error Handling Guides von Apple eher ein NSError Objekt mitschleppen.
    (Dann natürlich im Rahmen der Codehygiene einfach IMMER das Error-Objekt behandeln. Wenn's nil ist passiert einfach nix.)

    So ist es in Cocoa gedacht. Wobei das Mitschleppen zunehmend einfach wird, weil bei den weiteren Methoden einfach **error durchgereicht werden kann.
    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"?