iPhone-Tabellen-Performance

Christian Kruse

Die Performance von Tabellen auf den iPhone ist bei den meisten Programmen gelinde gesagt mies. Um jedoch die Programmierer in Schutz zu nehmen: das liegt nicht (nur) an ihnen. Apples iPhone-Tabellenframework funktioniert so, dass jedesmal, wenn eine Tabellenzelle in den sichtbaren Bereich scrollt, tableView:cellForRowAtIndexPath: aufgerufen wird. Diese Methode muss jedesmal neu das Zellenobjekt zurückgeben. Das gleiche gilt für etwaige Sektionen-Header und den Header und Footer. Das hat zwar den Vorteil, dass man maximale Flexiblität hat (man kann jederzeit die Zelle ändern), aber gleichzeitig auch den Nachteil, dass viel Performance verloren gehen kann. Deshalb sollte man sich an folgende Richtlinien halten:

So wenig wie möglich
Wenn eine Tabellenzelle erstellt wird, sollte in tableView:cellForRowAtIndexPath: so wenig wie möglich passieren. Informationen cachen und nicht jedesmal berechnen, wenig allocs, usw.
Die Zellenview vorbereiten
Eines der schlechtesten Sachen in Hinblick auf die Performance, die man machen kann, ist, die Zellenview jedesmal neu zu erstellen. Das kostet enorm viel Performance. Am besten wäre hier nach dem Model-View-Controller-Prinzip, dass man sich via View-Controller im loadView bzw. initWithNibName:bundle: eine View vorbereitet und die nur zurück gibt, mit ggfls. Anpassungen der UI-Objekte der View.
Keine (aufwendige) Arithmetik
Anfangs habe ich jedesmal, wenn ich eine Zelle zurückgeben sollte, ein Datum formatiert. Das ist eine ganz schlechte Idee, aufwendige Arithmetik ist der Performance-Fresser schlechthin. Solche Berechnungen sollten auf jedenfall gecached werden!
Lieber Speicher als CPU
Allgemein gilt: lieber etwas mehr Speicher nutzen und dafür die CPU weniger beanspruchen als umgekehrt. Die CPU ist sehr stark beansprucht beim Scrollen durch Tabellen.
Lieber UITableViewStylePlain als UITableViewStyleGrouped
UITableViewStyleGrouped mag zwar nett aussehen, hat jedoch den entscheidenden Nachteil, dass es langsamer ist. Durch die abgerundeten Ecken und die möglicherweise transparenten Headerviews gibt es viele transparente Bereiche, und Transparenz kostet Rechenzeit.
Lieber weißer Hintergrund als Transparenz
Transparenz kostet Rechenzeit, und das nicht wenig. Deshalb lieber einen weißen Hintergrund benutzen als Transparenz. Das gilt für quasi alle Objekte, auch UILabel, UIImageView oder UITextField!
Lieber nebeneinander als Übereinander
Meistens wird eine Tabelle über die GPU gezeichnet. Die GPU jedoch kommt nur schlecht klar mit Blending. Deshalb kostet es ziemlich viel Performance, wenn man Objekte übereinander bzw. überlappend anzeigen lässt. Das ist z. B. der Fall bei einer View mit transparenten UILabel.

Selbstverständlich gilt das selbe für tableView:viewForHeaderInSection:. Ihr seht, im wesentlichen läuft es darauf hinaus, dass man einfach sehr sparsam umgehen muss mit den (sehr begrenzten, da Embedded-Device) Ressourcen, die man hat.

Bei aufwendigen Zellenviews kann es passieren, dass trotz der Einhaltung der obigen „Richtlinien” noch Performance-Probleme auftreten. In diesem Fall kann es sinnvoll sein, die Zelle „selber” zu zeichnen. Wie genau das geht und warum das oft schneller ist, haben die Leute von Atebits beschrieben. Im Prinzip läuft es darauf hinaus, dass man durch das überschreiben von drawRect: die Zeichenaufgabe von der GPU auf die CPU verlagert und die GPU dann nur noch mit einer einzelnen, statischen View arbeiten muss, was deutlich performanter implementiert ist.