Ich werd langsam zu alt für so einen Mist ;-)

Ich schreibe hier nicht gern von meiner Arbeit. Mich versteht ja doch keiner. Aber manchmal ist es einfach zu schrill. Heute zum Beispiel fand ich einen wunderbaren Fehler leider erst nach über zwei Stunden. iPad Anwendung. Folgende Situation:
Ich habe ein UIView, das mit einer bestimmten Größe initialisiert wird. In dieses View werden im Nachhinein dynamisch Subviews und Controls hinzugefügt. Das View ist in einem SplitviewController eingebettet.
Damit das Ganze schön skaliert habe ich außerdem folgendes gemacht:

searchView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
searchView.autoresizesSubviews = YES;

und danach die Subviews hinzugefügt. Wenn man die Anwendung startet sieht optisch alles gut aus, die Controls sind am richtigen Fleck. Aber das Event-Handling funktionierte nicht mehr. Ich habe wirklich alles ausprobiert, bis ich testweise einen Button hinzugefügt habe. Dabei fiel mir auf, dass die Events doch ausgelöst wurden .. allerdings nur in einem sehr schmalen Streifen am oberen Ende des Button. Gottseidank habe ich überall rumgeklickt ;-) Danach habe ich das Flag UIViewAutoresizingFlexibleHeight weggelassen und siehe da, alles funktioniert wie erwartet. Wie es scheint, wird der View tatsächlich flexibel skaliert, seine Kind-Elemente jedoch nicht. Skaliert wird nur der Bereich in dem Events ausgewertet werden. Wir haben also zwei Views, eins mit den tatsächlichen Controls und eins mit den Grenzen des Event-Handlings. Die andere Erklärung ist die, dass Kind-Elemente einfach aus ihrem Parent herausragen können. Was auch immer passiert war, es war nicht auf Anhieb zu erkennen. Eher so auf Siebt- oder Achthieb.
Was lernen wir daraus?
- Öfter einchecken um funktionsfähige von kaputten Sourcecodeteilen besser unterscheiden zu können
- sich von der Annahme verabschieden, alles müßte so funktionieren wie man es sich gedacht hat
- manuelles Resizing muss in allen Ebenen der Viewhierarchie implementiert werden damit es zuverlässig funktioniert
- es ist verblüffend wieviel Zeit man in API-Spezialskills investieren muss, die man hinterher auf keiner anderen Plattform mehr gebrauchen kann

iPhone und Apple

In meinem "offiziellen" Job bin ich nun seit 8 Wochen (mit einigen Unterbrechungen) mit Softwareentwicklung für das iPhone beschäftigt. An dieser Stelle möchte ich kurz meine bisherigen Erfahrungen resümieren. Vorausschickend noch anzumerken ist, daß sich vieles wie Meckerei anhört. Das sollte nicht so sein. Ich bin dem Apple-Virus nicht anheim gefallen, verwende die Geräte privat nicht und habe daher einen neutralen Standpunkt. Es geht nicht darum, ob iPhones cool oder praktisch sind. Eher, was passiert wenn man dafür Software entwickelt.

iPhone Entwicklung - lohnt sich das denn?
Die Frage aller Fragen. Angesichts der unüberschaubaren Flut an Apps im Apple Store kann das Gefühl entstehen, daß dieser Markt komplett gesättigt ist. Das stimmt allerdings nicht, denn es werden immer noch Neugeräte (und damit Apps) verkauft. Allerdings - um "entdeckt" zu werden muß man entweder kostenlos (Blödsinn, wir wollen Geld verdienen) oder sehr gut sein. Wer früh kommt, verdient allerdings auch mit simpelsten Anwendungen Geld. Inzwischen geht das aus meiner Sicht für den Apple-Store / Consumermarkt nur dann, wenn es nur wenig Konkurrenz für die "Idee" gibt. Man muss berücksichtigen: Wenn man, sagen wir, in 2 Wochen eine sehr einfache App entwickelt (ganz optimistisch gerechnet) bei einem hiesigen, sehr konservativen Tagessatz von 500€ und noch die Hardwareeinstiegskosten (iMac Mini + iPod = 1.000€) + 100€ Entwicklerlizenz dazunimmt, liegt man bei einer initialen Investition von 6.100 €. Setzt man 1€ an muss die App 8.714-mal heruntergeladen werden (Apple Steuer: 30%), bevor man den break even erreicht. Ab diesem Zeitpunkt verdient man Geld. Aber nicht vergessen: Das ist der Consumer-Markt. Man ist zum Support verpflichtet. Bekommt man von nur einem Promille aller Anwender eine E-Mail (ganz dolle optimistisch), muß man bis zu diesem Punkt schon 8 Supportanfragen bearbeiten. Oh, lala. "Läuft" es einmal, wird die Anwendung in der Regel sehr schnell gecrackt und Plagiate tauchen auf, wie Kollegen von mir schon erfahren haben. Die Verkaufskurve zeigt dann unwillkührlich wieder nach unten. Ein großer Vorteil soll natürlich nicht verschwiegen werden. Als Entwickler im stillen Kämmerlein hat man mit dem AppStore eine fantastische Vertriebskette zur Verfügung. Die Kunden müssen nicht mehr gefunden oder geworben werden. Falls, tja, falls sie einen innerhalb der derzeit rund 200.000 angebotenen Apps selbst finden. :-)
Jetzt entwickeln wir nicht für den Consumer-Markt. Wir haben Geschäftskunden, die Masse ist nicht entscheidend. Allerdings bleibt die Gerätevielfalt dieselbe. Zur Anwendungsentwicklung auf einem Endgerät existiert eine preiswerte Alternative, und zwar die Implementierung einer für mobile Geräte optimierten Webseite. In vielen Fällen müssen ohnehin online auf Geschäftsdaten zugegriffen werden, eine Internetverbindung ist also erforderlich. Und ob HTC/Android, Berry oder iPhone, es läuft überall (mehr oder weniger ansehnlich) gleich. Im Funkloch tut sich dann nichts mehr, aber das ist dann vom Anwendungsfall abhängig. Man prüfe also, ob man sich das antun will ;)

Restriktionen
In der Apple-Welt lebt man auf einer Insel. Das ganze System ist darauf ausgelegt, dass der Anwender und der Entwickler ausschließlich Apple-Komponenten verwenden. Das fängt schon bei Kleinigkeiten an, wie dem Anschluß einer herkömmlichen PC Tastatur an einem Mac mini. Oder die Darstellung der Apple-Dokumentation in anderen Browsern außer Safari. Oder der Verbindung zu Windows Shares im Finder. Lauter kleine Ärgernisse, die man zwar irgendwann durch Googelei lösbar sind, aber trotzdem nerven, weil es sich um bewußt ausgelegte Stolpersteine handelt. Wer will sich schon gern die Arbeitsweise vorschreiben lassen. Ganz extrem ist natürlich dann die Entwicklungsumgebung fürs iPhone, ausschließlich mit XCode auf einem Mac Betriebssystem. Das hat keine technischen Gründe. Besonders zynisch dabei ist aus meiner Sicht, daß die darunterliegenden Komponenten aus der Unix-Welt stammen und wie der GCC beispielsweise der GPL unterliegen. Alles sicherlich rechtens, komisch riechen tut es trotzdem.

Entwicklung
Wie entwickelt man denn nun? XCode entpuppt sich als vollwertige und moderne Entwicklungsumgebung. In der Produktivität bleibt das Programm trotzdem meilenweit hinter Visual Studio zurück, was an dem umständlichen Debugger und am Fehlen von Funktionen wie "Edit & Continue" und der etwas zähen Intellisense Unterstützung liegt. Objective-C hätte ich mir schlimmer vorgestellt, ich habe mich trotz meines etwas vorgerückten Alters relativ schnell damit zurechtgefunden. Aber die Bibliotheksfunktionen! Immer wieder herrlich, wenn man selbst primitive Dinge wie das Zusammenfügen von Strings in der Doku nachschlagen darf, nur weil man den Funktionsnamen wieder vergessen hat. Man merkt auch, daß die Programmiersprache nur auf C übergestülpt ist. Probleme wie die Korruption des Speichers oder Stacks, Zugriff auf de-allokierte Objekte und ähnliche Schweinereien die ich schon ziemlich verdrängt hatte sind wieder an der Tagesordnung. Mit den damit verbundenen Programmabstürzen. Es ist problemlos möglich, eine Maske im Interface Builder so zu konfigurieren, daß das Programm an einer vollständig irreführenden Stelle einfriert. Nicht falsch verstehen. Das ist alles lösbar, nur würde man seine Zeit gern für andere Dinge verwenden. Im Grunde wird man ständig daran erinnert, daß man nicht wiederverwendbare Fähigkeiten für eine Insellösung aufbaut. So könnte man natürlich auch bei .NET, Java und ähnlichem argumentieren, nur sind die Inseln dort eher.. Kontinente.

Lernen
Wie ein guter Student habe ich mir zunächst ein Buch bestellt. "iPhone SDK Application Development" von Jonathan Zdziarski. Ein ehemaliger iPhone Hacker, ich dachte, das ist ganz sinnvoll. Leider erfüllte es nicht ganz meine Erwartungen. Die API hat sich inzwischen etwas verändert, so daß viele Beispiele etwas veraltet sind. An vielen Stellen hätte ich mir auch etwas mehr Tiefe gewünscht, dafür dann evtl. andere Bereiche wie Audio weggelassen. Auch der Index hilft nur begrenzt bei Problemen. Ein Buch im Sinne eines "Cookbook", wie es für viele anderen Sprachen gibt ist sicherlich sinnvoller für den Einsteiger. Für Detailfragen muß man ohnehin auf Google zurückgreifen. Ich frage mich inzwischen ernsthaft, ob Bücher in dieser Form überhaupt noch einen praktischen Nutzen haben, außer auf der Biographie des Autors einen äußerst positiven Eindruck zu hinterlassen. (https://www.puls200.de/wp-content/plugins/Sterne/img/icon-rating-star_f_blue.gifhttps://www.puls200.de/wp-content/plugins/Sterne/img/icon-rating-star_f_blue.gifhttps://www.puls200.de/wp-content/plugins/Sterne/img/icon-rating-star_f_blue.gifhttps://www.puls200.de/wp-content/plugins/Sterne/img/icon-rating-star_n_blue.gifhttps://www.puls200.de/wp-content/plugins/Sterne/img/icon-rating-star_n_blue.gif).

Hack!! Arrrgh

Womit man so seinen Nachmittag verbringen kann. Alles fing damit an, daß der GeoURL Button unten rechts nicht mehr funktionierte. Die Tags waren drin, also an was konnte es liegen? Der GeoURL Support schlug vor, doch mal den W3 Validator auszuprobieren. Hä? Ich verwende doch Software von der Stange, aber warum nicht.. und siehe da, der Validator sagt, er könne mit Content-Type null nichts anfangen. Ich schaue in den Header des Themes, der Content-Type wird explizit gesetzt, genauso wie im Quellcode der Seite. Doch irgendwas ist komisch mit dem header.php..
Dann sehe ich es: In der ersten Zeile steht was völlig merkwürdiges:

<?php /**/eval(base64_decode('aWYoZnVuY3Rpb25 ...

Etwas primitive Tarnung, das Skript (ziemlich obfuscated) in einem Base64 String. Das wiederum rief ein in einem entlegen Winkel meiner Webseite plaziertes Skript auf, das wiederum eine 100kb Datei dekodierte und dann evaluierte. Was das genau tut habe ich noch nicht herausgefunden. Eins jedenfalls hat die Infektion geschafft: Alle meine PHP Seiten waren damit infiziert (.php5 Seiten aber nicht!) Das hatte anscheinend nichts mit WordPress zu tun. Dem Änderungsdatum zufolge war das bereits vor einem halben Jahr geschehen. Ich hatte nichts bemerkt!
Wie das ganze hereingekommen ist weiß ich leider noch nicht. Ich vermute, daß es an der sehr alten WordPress-Version vom Blauen Heft liegt, speziell den dort verwendeten Javascript Editor (in dessen Unterverzeichnis das "Mutter-Skript" untergebracht war).
Ich mußte das natürlich zum Anlaß nehmen beide Blogs auf WordPress 3.0 anzuheben, alle anderen PHP Skripte manuell zu bearbeiten und mal wieder kräftig aufzuräumen. Aufräumen ist ja immer eine super Sache. Man muß es nur ab und zu tun. Danach funktionierte auch der GeoURL Knopf wieder.

Zusammenfassung:

  • Ab und zu in den PHP Quellcode schauen
  • Regelmäßige Backups der Files und Datenbank
  • Nie alte Versionen herumliegen lassen

Links dazu aus dem Web, hier und hier. Peinlich wenn man sich anschaut wie alt das schon ist..

Denglish for Programmers

In vielen Software-Firmen gibt es die Anweisung, daß Variablennamen, Kommentare und ähnliche Dinge in der Software auf Englisch verfaßt sein müssen. Bei uns beispielsweise auch. Warum eigentlich? Um den Chinesen die Einarbeitungszeit nach der Raubkopie zu verkürzen? Ich persönlich bin der Ansicht, daß diese Regel eher kontraproduktiv ist. Vor allem deshalb, weil die wenigsten Entwickler in meinem Kollegenkreis über die notwendige Fähigkeit verfügen, verständliche Kommentare zu setzen. Oft gleichen die Einträge dann Wort-für-Wort Übersetzungen aus Leo. Das hat schließlich einige dazu getrieben, einen Art Sprach-Kompromiß einzugehen. Hier eine Perle, die ich heute entdeckt habe:

private const string pleaseChoiseALiefernachweisText = "textSelectLiefernachweis";

Yeah! Ich soll bitte einen Liefernachweis wählen, äh, ich meine natürlich choisen. Muahaha. Also, das ganze ist wirklich völliger Quark. Setzt die Kommentare doch so, daß diejenigen sie verstehen, für die sie gedacht sind. Genau, die Kollegen. Die meisten von denen verstehen zwar auch denglisch, nur besteht immer das Risiko eines Mißverständnisses. Ich für meinen Teil habe von jeher diese Arbeitsanweisung ignoriert.

Daily WTF

Die Überschrift habe ich hier geklaut. Inzwischen ist es so daß das Monster, welches ich schon seit Jahren betreue, der meisten Schauerlichkeiten beraubt wurde. Aber hin und wieder tauchen einzelne Perlen auf, so wie das hier:

public bool Enthalten
{
     get { return !this.entity.Enthalten.Value; }
     set
          {
            if (!this.entity.Context.IsChangeTrackingEnabled)
                   this.entity.Context.EnableChangeTracking(true);
            this.entity.Enthalten.Value = !value;
          }
}

Die entsprechende Datenspalte trug übrigens den bedeutungsschwangeren Namen "Nicht". Nicht was? Nicht geglückt? Nicht rechtzeitig ins Bett gekommen? Wir werden es nie erfahren.

Tippsler

Als Fan des gleichsam sehr unterhaltsamen wie oftmals arschgeigenmäßig arroganten, aber gewiß genialen Steve Yegge möchte ich an dieser Stelle auf seinen neusten Artikel hinweisen. Den kann man auch lesen ohne vorher einen 10-jährigen Lisp Kurs gemacht zu haben. Es geht darum daß Leute, die mit Rechnern arbeiten, unwillkürlich in Tipper und Tipp-Legastheniker (Suchsystem) unterteilt werden, anhand dessen ihre Produktivität intuitiv eingeschätzt wird. Ich will das jetzt nicht nacherzählen, ich habe mich nur gefragt wie man das herausbekommt. Jeder hat sein eigenes System und seine eigene Einschätzung. Ein entscheidendes Merkmal ist sicher ob man tippen kann ohne hinunterzusehen. Und dann natürlich die Geschwindigkeit. Eine extrem heitere Methode etwas darüber zu erfahren ist der Typeracer. Leider gibt es nur englische Texte, so daß wir mit unseren deutschen Tastaturen etwas benachteiligt sind, aber ich kann nur empfehlen es einmal auszuprobieren. Wer's wissen will: Mein persönlicher Durchschnitt liegt zwischen 60 und 70 W(ords) P(er) M(inute). Um es an die Spitze zu schaffen sind allerdings astronomische 140 WPM (!) nötig.

Steinchenregen

Die "Technopolis" in der Nähe von Antwerpen ist ein großes Technikmuseum zum Anfassen. Dort gibt es Wasserspiele, optische und akustische Versuche, die man machen kann und die vor allem Kindern erklären, wie die Sachen funktionieren. Mittendrin habe ich etwas gefunden, daß man sicher mit nicht allzu aufwendigen Mitteln selbst bauen könnte. Vorausgesetzt, man hat Zeit. Der Effekt ist jedenfalls beeindruckend. Man benötigt: Einen PC, einen Beamer, eine Leinwand und eine Kamera. Ham wa schon daheim? Na super. Mir fehlt noch der Beamer und die Leinwand. Der Beamer stellt eine Simulation dar, z.B. fallende Steinchen. Die Kamera nimmt das Bild auf der Leinwand auf und filtert alles heraus, das nicht von der Simulation erzeugt wird, z.B. der Schatten einer Person, die vor der Leinwand steht. Das daraus resultierende Objekt wird in die Simulation als Festkörper mit eingebunden, auf dem die fallenden Steinchen dann liegenbleiben.
Der Effekt ist phänomenal: Man steht vor der Leinwand, breitet die Arme aus und über einem wächst der Steinhaufen immer mehr an. Tritt man beiseite wird das Objekt aus der Simulation entfernt und die aufgestauten Steinchen fallen herunter. Das ganze gab es dann noch als Variation bei der durch die Simulation feste Objekte auf einer Oberfläche dargestellt wurden (z.B. Billardkugeln auf einem Tisch). Mit Gesten konnte man dann die Kugeln anstoßen, sobald der eigene Schatten das projizierte Bild berührte. Nächster logischer Schritt wäre dann die Implementierung eines "Pong" Clones.

Sterne Plugin: neue Version 1.1

Ein kleines Update für das Sterne Plugin:
Die Farben sollten jetzt etwas besser rüberkommen als in der Version 1.0.
Gelbe Sterne sind jetzt wirklich gelb: SternSternSternSternSternSternStern :-)
Zur Installation: Wer bereits Version 1.0 einsetzt muß nur die Datei Sterne.php ersetzen. Außerdem sollten die bereits erzeugten Sterne gelöscht werden (werden bei der nächsten Anzeige der betreffenden Seite neu erzeugt). Das sind die Dateien unter /wp-content/plugins/Sterne/.. und zwar alle in der Form:

icon-rating-star_f_Zahl_Zahl_Zahl.png also z.B. icon-rating-star_f_255_255_0.png

Bitmap vs. Image

In C# existiert eine abstrakte Basisklasse für Bilder genannt Image. Speichere ich nun ein geladenes Bild als Image in einer anderen Klasse, scheint es irgendwann vom GarbageCollektor abgeräumt zu werden.
d.h. sowas:

class Ship {
private Image image;
// ..
}

Am Anfang wird das Bild geladen und mehrfach verwendet. Nur irgendwann ist es weg (null)!. Ernsthaft, ich dachte ich spinne. Speichert man das Bild als Bitmap funktioniert alles wie erwartet. Ich habe recht lange gegoogelt und nichts gefunden bis ich durch Zufall diese Änderung gemacht habe und auf einmal funktionierte alles. Zum Haareraufen! (daher paßt es gut hierher ;-) )

Remoting für Runaways

Ein jeder, der sich noch vage an Dinge wie Corba erinnern kann weiß, welch nette Idee hinter verteilten Objekten steckt: Ein Objekt wird als verteilt gekennzeichnet und wenn es mit einem geeigneten Parameter erzeugt wird, entsteht es auf einem anderen Rechner. Für die Anwendung sieht es aber wie ein lokales Objekt aus, auf das ganz normal zugegriffen werden kann. Hört sich toll an, führt in der Praxis jedoch zu allerlei Problemen, da die Zugriffe von den Launen des Netzwerks abhängig sind. Unter .NET wurde das Rad nochmal unter der Bezeichnung remoting neu erfunden.

Der Zugriff auf ein entferntes Objekt ist einfach, ein paar Zeilen genügen:

string url = string.Format("tcp://{0}:40506/LPT_IO_Server",

                    this.address);

this.remoteObj = (BaseRemoteObject)

   Activator.GetObject( typeof(BaseRemoteObject), url);

Aber jetzt kommt das dicke Ende: Wenn der Server unten ist, bleibt die Anwendung beim Zugriff auf eines der Felder einfach hängen. Es gibt keine Möglichkeit, einen connection timeout einzustellen. Es dauert dann etwa 45 Sekunden, bevor eine SocketException anzeigt, daß da was schiefging. Nicht gut für eine Anwendung mit Benutzerinteraktion. Man kann sich allerdings "dreckig" behelfen, indem man den Zugriff in einen eigenen Thread packt:

Exception setException = null;

System.Threading.Thread setThread = new System.Threading.Thread( delegate()

{

    try

    {

        this.remoteObj.setValue(value);

    }

    catch (Exception ex)

    {

        setException = ex;

    }

});

setThread.Start();

if (setThread.Join((int)5000))

{

    if (setException == null)

    {

        // everything seems to be A-OK

        return true;

    }

}

return false;

Das sieht nicht nur scheußlich aus sondern ist es auch. Aber so funktioniert es wenigstens. Jetzt bekommt man zwar auch eine Exception, aber asynchron. Ich finde es verwunderlich, daß M$ hier auf ein besseres Interface verzichtet hat. Hat man sich aber soweit vorgewühlt, ist die remoting Lösung eine sehr praktische Sache. Keine Socketprogrammierung, kein Marshaling, einfach nur die Objekte deklariert und es kann losgehen.
Meine Anwendung sieht folgendermaßen aus: Auf bis zu 8 entfernten Rechnern laufen Windows Services, die die Datenkanäle des LPT Ports schalten können. Das Interface besteht aus einer einzigen Funktion: setValue(byte). Auf einem zentralen Rechner kann man in einem Fenster alle entfernten Ports einzeln oder gleichzeitig schalten. Aus Remoting-Sicht ist dieser Rechner also der Client, der zu den entfernten Servern eine Verbindung aufbaut. Es ist wirklich phänomenal, wie schnell man zu einem Ergebnis kommt: Hätte ich o.g. Probleme nicht gehabt, wäre ich nach 5 Stunden (einschließlich der Setups) fertig gewesen.