Programmieren auf dem iPhone

Phillipp Bertram

Voraussetzungen

  • Eine Kopie des Apple iPhone SDK. Diese kann im Apple's iPhone Dev Center kostenlos heruntergeladen werden, sofern man dem Apple Developer Programm beigetreten ist.
  • Ein iPhone/iPod-Touch/iPad. Obwohl Apple einen Simulator als Teil des SDK's mitliefert, wird ein konkretes Gerät benötigt, auf dem die Software getestet werden kann.
  • Eine Apple iPhone Developer Lizenz. Erst wenn man dem Developer Programm beigetreten ist, kann die programmierte Software auf dem iPhone getestet werden. Mitglieder erhalten ein Zertifikat, dass ihnen erlaubt ihre Applikationen auf die Endgeräte zu installieren um sie zu testen. Das Programm kostet 99$ im Jahr für Einzelpersonen und Unternehmen.
  • Ein Intel-basierter Macintosh mit Leopard. Das SDK erfordert einen Macintosh mit Leopard OS X 10.5.3 oder höher als Betriebssystem.
  • Wenigstens einen verfügbaren USB 2.0 Anschluss. Die eigenen Applikationen werden über einen USB 2.0 Anschluss auf das Gerät installiert.
  • Internetverbindung. Mit einer Internetverbindung können Anwendungen mit einer WiFi oder EDGE Verdingung getestet werden.
  • Kenntnisse in Objective-C. Die Sprache ist eine Obermenge von C mit objektorientierten Erweiterungen und gehört zur Standardsprache von Apple.

Objective-C

Objective-C (auch ObjC) ist eine reflexive, objektorientierte Programmiersprache, die die Programmiersprache C um Smalltalk ähnliches Messaging erweitert. Sie bildet eine Obermenge von C, es kann also jedes in C geschriebenes Programm mit einem ObjC-Compiler kompiliert werden. Heute wird es in erster Linie auf Apple's Mac OS X und iOS verwendet: beide Umgebungen basieren auf dem OpenStep-Standard, sind mit ihm allerdings nicht konform. ObjC ist die primäre Sprache der Apple Cocoa-API und war ursprünglich die Hauptsprache auf NeXTStep OS.

Hello Objective-C

Die folgenden Beispiele sollen einen kleinen Einblick in die Programmiersprache Objective-C geben. Gestartet wird mit einer klassischen Hello-Objective-C-Anwendung.

#import <Foundation/Foundation.h>
 
int main (int argc, const char *argv[])
{
   NSLog(@"Hello, Objective-C");
   return(0);
}

HelloWorld Beispiel

Die Foundation.h ist eine Libary, die wesentliche Funktionen enthält und wird daher in den allermeisten Fällen importiert. NSLog gibt einen String auf die Konsole aus, der in diesem Fall 'Hello, Objective-C' lautet. Wird die Main-Methode mit 0 beendet, bedeutet dies nur, dass das Programm erfolgreich durchlaufen wurde.

Objekt Zugriffe

Anders als in Java werden die Methoden der Objekte nicht über eine Punkt-Notation angesprochen, sondern als message an das Objekt geschickt. Folgender Java-Code

anObject.methodeOhneParameter();
anObject.methodeMitParameter(5, 10);

sieht in ObjC also entsprechend so aus:

[anObject methodeOhneParameter];
[anObject methodeMitParameter:5 undWeitererParameter:10];

Speicherverwaltung

Wie bereits erwähnt, muss man sich bei der Programmierung auf dem iPhone selber um die Speicherverwaltung kümmern, da es - anders als in Java - keinen Garbage Collector gibt. Daher müssen alle allokierten Objekte auch wieder freigegeben werden. Eine Faustregel besagt, dass zu jedem alloc, ein release folgen muss, wie in dem folgenden Beispiel gezeigt ist.

String *myString = [[NSString alloc] initWithFormat:@"Ich bin ein String"];
 
// ... ganz viel Code ...
 
[myString release];

Objekte können in ObjC über autorelease automatisch wieder freigegeben werden. Allerdings sollte das nur in wenigen Fällen angewendet werden, da es hier leicht zu Fehlern kommen kann. Der folgende Code zeigt, wie autorelease eingesetzt wird.

String *myString = [[NSString alloc] initWithFormat:@"Ich bin ein String"];
[myString autorelease];
 
//alternativ in einer Zeile
String *another String = [[[NSString alloc] initWithFormat:@"Ich bin ein weiterer String"] autorelease];

Strings in Objective-C müssen immer mit einem @ versehen werden. So wird zwischen ObjC- und C-Strings unterschieden, bei denen wie gewohnt das Zeichen entfällt.

NSLog(@"Objc String");
printf("C String");

Ein weiterer Unterschied zu C oder anderen Programmiersprachen ist der Datentyp Boolean. Hier kann dieser Datentyp nicht die Werte true oder false annehmen, sondern YES oder NO. Sonst ist das Verhalten in etwa gleich.

Properties und Aufbau einer Klasse

Eine noch zu erwähnende Eigenschaft von ObjC sind die sogenannten Properties. Guter Programmierstil ist, wenn Attribute nicht direkt, sondern über Accessoren modifiziert und abgerufen werden. Um nicht für jedes Attribut einen solchen Getter und Setter schreiben zu müssen, gibt es die Properties.

// EineKlasse.h
@interface EineKlasse:NSObject
{
   String *aString;
}
 
@property(retain) String *aString;
 
-(void) methodeOhneParameter;
-(void) methodMitParameterA:(String*) str andParameterB:(String*) anotherString;
-(String*) methodeMitRueckgabewert;
 
@end
 
// EineKlasse.m
#import EineKlasse.h
 
@implementation EineKlasse
 
@synthesize aString;
 
-(void) methodeOhneParameter
{
   // mach was
}
 
-(void) methodMitParameterA:(String*) str andParameterB:(String*) anotherString
{
  // mach was mit den Parametern
}
 
-(String*) methodeMitRueckgabewert
{
   return @"Hallo Welt";
}
 
-(void) dealloc
{
   [aString release];
   [super dealloc];
}
 
@end

Aufbau von Klassen und Methoden

Das Beispiel zeigt, wie in etwa eine Pseudo-Klasse aufgebaut ist. Eine Klasse besteht immer aus einem Interface (EineKlasse.h) und einer Implementation (EineKlasse.m). Für die meisten Anwendung, die für das iPhone programmiert werden, ist es sinnvoll, die Klasse von NSObject erben zu lassen. NSObject ist wie Object in Java eine Basisklasse, von der alle Klassen abstammen sollten, die iPhone spezifisch sind. Sie enthält die essenziellen Methoden zur Speicherverwaltung (wie dealloc) und noch weitere Eigenschaften.

Attribute werden in der Header-Datei innerhalb der geschweiften Klammern deklariert. Danach besteht die Möglichkeit zum einen Properties anzulegen und zum anderen Methoden-Signaturen aufzuführen, wie es schon aus C bekannt ist. Properties werden mit dem Schlüsselwort @property eingeleitet, gefolgt von wenigen Einstellungsmöglichkeiten innerhalb der Klammer (z.B. retain). Anschließend muss der Datentyp und der Name des Attributs aufgeführt werden, der der Property zugewiesen wird. Eine weitere Eigenschaft der Properties ist, dass diese Attribute - wie in Java - auch per Punkt-Notation angesprochen werden können.

Die Implementierung geschieht wie in C in der .m Datei. Hier müssen die Properties noch synthetisiert werden. Das bedeutet nur, dass dem Compiler mit dem Schlüsselwort @synthesize kenntlich gemacht wird, dass er die Getter- und Setter-Methoden beim Compilieren einfügen soll.

Die Methode dealloc wird immer dann aufgerufen, wenn ein Objekt dieser Klasse zerstört wird. Hier sollte der gesamte Speicher freigegeben werden, den das Objekt allokiert hat.

Apple's iPhone SDK

Apple entwickelte für das iPhone eine mobile Variante seines Cocoa-Frameworks namens Cocoa Touch. Das SDK wird zusammen mit einer neuen Version von Apple's integrierter Entwicklungsumgebung Xcode ausgeliefert. Darin enthalten ist auch ein iPhone-Simulator, der es weitestgehend ermöglicht, die Anwendungen während der Entwicklungsphase auf dem Mac zu testen. Der Vertrieb der Programme erfolgt über den App Store oder über iTunes. Die Entwickler können den Preis für ihre Software selbst festlegen, Apple nimmt jedoch 30 Prozent davon als Provision. Während das SDK selbst kostenlos von Apple's Entwicklerseiten bezogen werden kann, ist für die Veröffentlichung im App Store ein kostenpflichtiges Entwicklerkonto zum Preis von 99 $ (Standard) oder 299 $ (unternehmensinterne Anwendungen) pro Jahr erforderlich.

  • XCode ist das wichtigste Werkzeug im iPhone Development Arsenal. Es bietet eine umfassende Projektentwicklung und Management-Umgebung, mit umfassender Dokumentation, einem Editor und einem graphischen Debugger.
  • Instruments zeigt, wie iPhone Applikationen im Hintergrund ablaufen. Hiermit können Probleme gefunden und die Performance optimiert werden. Instruments bietet grafische, zeit-basierte Plots die zeigen, wo die Applikation die meisten Ressourcen verbraucht.
  • Der Simulator läuft auf dem Macintosh und ermöglicht das Testen der Applikationen auf dem Desktop. Dazu muss kein iPhone angeschlossen sein. Der Simulator bietet die gleiche API, die auch auf dem iPhone verwendet wird.
  • Der Interface Builder bietet ein Rapid-Prototyping-Werkzeug mit dem man die Benutzeroberflächen designen und über vordefinierte Schnittstellen im Quellcode diese Elemente verlinken kann. Man erstellt also seine Oberflächen mit visuellen Design-Werkzeugen und verlinkt diese Bildschirmelemente mit Objekten und Methodenaufrufe aus der Applikation. Da dieser allerdings im Rahmen dieser Projektarbeit nicht benutzt wurde, wird an dieser Stelle auch nicht weiter auf ihn eingegangen.

Application Life Cycle

Abb. 1: iPhone Application LifeCycle. Quelle: http://www.codeproject.com/KB/iPhone/ApplicationLifeCycle.aspx

Komponenten der Applikation

Compilierte iPhone Applikationen "leben" in sogenannten Application-Bundles. Dies sind nichts weiter als Ordner mit einer .app Endung, die eben alle Ressourcen eines Programms enthalten. Die Hierarchie dieser Bundles ist einfach gestrikt. Alle Materialien befinden sich in der obersten Ebene des Ordners. Es können auch Unterordner erzeugt werden, die das Projekt besser strukturieren und organisieren, allerdings folgen diesen eigenen Unterordnern keine Standards. Das iPhone SDK unterstützt hierfür die NSBundle Klasse. Diese Klasse stellt unter anderem Methoden bereit, mit denen man auf die Dateien in dem Bundle zugreifen kann.

Die Executable Applikation Datei befindet sich ebenfalls in der obersten Ebene des Bundles. Sie enthält Ausführungsrechte, sodass eine Applikation nur dann geladen und gestartet werden kann, wenn es mit einem offiziellen Entwickler Zertifikat ausgewiesen wurde.

Wichtiger Bestandteil einer Applikation ist die Property-List (Info.plist). Sie beinhaltet Key-Value Daten für viele unterschiedliche Zwecke und kann diese entweder in einem lesbaren, textbasierten oder komprimierten, binären Format abspeichern. Hier wird z.B. spezifiziert, welches die auszuführende Datei ist oder wie der Name des Bundles ist.

Grundgerüst einer iPhone Applikation

Fast alle iPhone Applikationen enthalten ein paar Schlüssel-Dateien, die in der unteren Abbildung aufgeführt sind - eine Main, eine ApplicationDelegate und eine ViewVontroller Komponente. Diese Dateien stellen alles nötige bereit, um eine einfache Anwendung zu erstellen.

Abb. 2: Grundgerüst einer iPhone Applikation. Zu finden in 'The iPhone Developer's CookBook', S.18

Die main.m hat zwei Aufgaben. Erst erstellt es einen Autorelease-Pool für die Applikation. Dieser Pool sammelt alle Objekte, die mit autorelease gekennzeichnet wurden und gibt den Speicher am Ende wieder frei. Danach wird der Application Event Loop aufgerufen, der unter anderem den Namen der ApplicationDelegate als Parameter übergeben bekommt.

int main(int argc, char *argv[])
{
  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  int retVal = UIApplicationMain(argc, argv, nil, @"MyAppDelegate");
  [pool release];
  return retVal;
}

Die UIApplicationMain Funktion ist eine Art Einstiegspunkt des Programms. Mehr muss man nicht über die main.m wissen. Diese wird automatisch von dem SDK generiert und es müssen in der Regel keine Änderungen an der Datei mehr vorgenommen werden.

Ein ApplicationDelegate implementiert, wie das Programm auf kritische Punkte im Application Life Cycle reagieren soll. Sie ist für das Initialisieren des Window-Systems beim Start verantwortlich und behandelt unter anderem Speicherwarnungen. Die wichtigsten Delegate-Methoden sind hier aufgeführt:

  • applicationDidFinishLaunching: Diese Methode wird als erstes aufgerufen, nachdem die Applikation initiiert wurde. Nach dem Start ist hier der Bereich, in dem das Basis Fenster (window) erstellt, dessen Inhalt gesetzt und sichtbar gemacht wird.
  • applicationWillTerminate: Diese Methode wird immer dann aufgerufen, wenn die Applikation beendet wird. In der Regel verwendet man diese Methode um z.B. Daten zu speichern, upzudaten oder auch Dateien zu schließen.
  • applicationDidRecieveMemoryWarning: Wird diese Methode aufgerufen, muss die Applikation so viel Speicher wie möglich freigeben. Falls kein Speicher freigegeben wird oder werden kann, wird das Programm unerwartet beendet.

Im iPhone Programmier-Paradigma stellen die ViewController das Herzstück der Anwendungen dar. Hier wird normalerweise das Verhalten der Applikation implementiert, wie sie auf Userinteraktionen reagieren soll. Wenn kein Interface Builder verwendet wurde, werden auch hier die Views geladen und aufgebaut.

Beispielanwendung

Mit diesem Beispiel soll gezeigt werden, wie man eine kleine iPhone Applikation erstellt. Es wird kein Interface Builder verwendet, sondern die Views werden innerhalb eines ViewControllers manuell zusammengebaut und geladen. Das Programm besteht hierbei lediglich aus einem Button, der beim Drücken den Inhalt eines Labels ändert.

Mit XCode wird ein neues Window-based-Applikation-Projekt namens 'HelloWorld' für iPhone OS angelegt. Im Ordner Classes sollten sich nun zwei Dateien mit der Beizeichnung HelloWorldAppDelegate befinden. Da kein Interface Builder verwendet wird, kann die 'HelloWorldViewController.xib' gelöscht und die main.m wie unten aufgeführt geändert werden. Als nächstest wird ein ViewController namens HelloWorldViewController eingefügt. Dieser enthält später den Button, das Label und die nötigen Methoden. Die Beispielanwendung hat letztendlich folgendes Aussehen:

int main(int argc, char *argv[])
{
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   int retVal = UIApplicationMain(argc, argv, nil,@"HelloWorldAppDelegate");
   [pool release];
   return retVal;
}

main.m

#import <UIKit/UIKit.h>
 
@interface HelloWorldViewController : UIViewController
{
	UIView	 *contentView;
	UILabel  *label;
	UIButton *button;	
}
 
- (void)buttonPressed;
 
@end

HelloWorldViewController.h

#import "HelloWorldViewController.h"
 
@implementation HelloWorldViewController
 
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
   // Init ContentView
   contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
   contentView.backgroundColor = [UIColor lightGrayColor];
 
   // Init Label
   label = [[UILabel alloc] initWithFrame:CGRectMake(50.0f, 250.0f, 200.0f, 30.0f)];
   label.text = @"Hello World";
   label.textAlignment = UITextAlignmentCenter;
   label.backgroundColor = [UIColor clearColor];
 
   // Init Button
   button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; button.frame = CGRectMake(100.0, 180.0, 100.0, 30.0);
   button.titleLabel.font = [UIFont fontWithName:@"Helvetica" size:15.000];
   [button setTitle:@"Press Me" forState:UIControlStateNormal];
   [button addTarget:self
           action:@selector(buttonPressed)
	   forControlEvents:UIControlEventTouchUpInside];
 
   // Add label and button to contentview
   [contentView addSubview:label]; [contentView addSubview:button];
 
   // set the view
   self.view = contentView;
}
 
- (void)buttonPressed
{
   label.text = @"Button Pressed!";
}
 
- (void)dealloc
{
  [contentView release];
  [label release];	
  [super dealloc];
}
 
@end

HelloWorldViewController.m

#import <UIKit/UIKit.h>
 
@class HelloWorldViewController;
 
@interface HelloWorldAppDelegate : NSObject <UIApplicationDelegate> {	
   UIWindow		    *window;
   HelloWorldViewController *hwvc;
}
 
@property (retain) UIWindow *window;
 
@end

HelloWorldAppDelegate.h

#import "HelloWorldAppDelegate.h"
#import "HelloWorldViewController.h"
 
@implementation HelloWorldAppDelegate
@synthesize window;
 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
 
   window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
   hwvc = [[HelloWorldViewController alloc] init];
 
   [window addSubview:hwvc.view];
   [window makeKeyAndVisible];
 
    return YES;
}
 
- (void)dealloc {
   [window release];
   [hwvc release];
   [super dealloc];
}
 
@end

HelloWorldAppDelegate.m

Im HelloWorldAppDelegate wird zunächst ein UIWindow generiert. UIWindow ist gleichbedeutend mit UIView mit dem Unterschied, dass UIWindow nur ein einziges mal existiert und die unterste Ebene der View Hierarchie darstellt. Alle weiteren Views werden quasi auf das window gelegt, wie auch in diesem Fall die View des HelloWorldViewController's mit der Zeile [window addSubView:hwcv.view]. Schließlich wird das window noch sichtbar gemacht.

Das Herz der Anwendung steckt jedoch in dem HelloWorldViewController. Im Interface sind lediglich die drei GUI-Elemente und eine Methode namens buttonPressed aufgeführt. Beim Laden der View des HelloWorldViewController's wird die loadView Methode aufgerufen und daher befindet sich hier auch die Initialisierung der Objekte. Zunächst wird eine content-View erstellt, die alle grafischen Objekte aufnehmen soll. Die Hintergrundfarbe wird auf hellgrau gesetzt. Das Label erhält Größe, Position und einen initialen Text. Der Button erhält ebenfalls eine Größe, eine Position, einen Text und ist vom Typ RoundedRect. Über addTarget wird eine Methode registriert, die aufgerufen wird, sobald der Benutzer den Button drückt und wieder loslässt UIControlEventTouchUpInside. Diese Methode macht nichts weiter, als den Text des Labels in ' Button Pressed! ' abzuändern. Schließlich werden alle allokierten Objekte in der Methode dealloc wieder freigegeben.


Page last modified on March 21, 2011, at 10:41 PM