Die Entwicklung auf der iPhone-Platform ist alles andere als trivial. Trotzdem entstanden in den letzten zweieinhalb Jahren mehr als 250 000 iPhone-Apps. Eine Analyse der am häufigsten verwendeten Wörter bei einer negativen App-Bewertung zeigt, dass vor allem Apps schlecht abschneiden, die abstürzen oder nicht wie versprochen funktionieren. Die Ursachen vieler Fehler liegen in falsch verstandenen Konzepten zur Speicherverwaltung oder der verwendeten Frameworks. Welche Fehler häufig gemacht werden und wie man sie aufspürt und vermeidet, soll dieser Vortrag zeigen.
1. Pitfalls bei der iOS-Entwicklung
Dipl.-Inform. (FH) Sven Günther
Agiler Senior Softwareentwickler
sven.guenther@it-agile.de - @iNevs
http://www.it-agile.de
2. About
Softwareentwickler seit 1997
erste Programmiersprache: C
Objective-C seit 2007
iOS Entwicklung seit 2009
Berater und Coach für agile Softwareentwicklung
gegründet 2005
Sitz in Hamburg und München
32 Mitarbeiter
Beratung, Schulung, Coaching in agilen Methoden
Scrum, Kanban, xTreme Programming
Develop With You
Develop 4 You
Sven Günther http://www.it-agile.de 2
3. Analyse der Crash-Reports
Speicherprobleme
Speicher ging aus
kein Response auf Memory-Warnings
System hat Programm abgeschossen
Sven Günther http://www.it-agile.de 3
4. Analyse der Crash-Reports
Speicherprobleme
Speicher ging aus
kein Response auf Memory-Warnings
System hat Programm abgeschossen
Crashes
Threading-Probleme
Zugriff auf freigegebenen Speicher
Sven Günther http://www.it-agile.de 3
8. Speicherverwaltung
iPhone: 256MB RAM
Speicher ist begrenzt und daher kostbar
im Gegensatz zum Mac / PC kann kein virtueller Speicher genutzt werden
Sven Günther http://www.it-agile.de 4
9. Speicherverwaltung
iPhone: 256MB RAM
Speicher ist begrenzt und daher kostbar
im Gegensatz zum Mac / PC kann kein virtueller Speicher genutzt werden
Sven Günther http://www.it-agile.de 4
10. Speicherverwaltung
iPhone: 256MB RAM
Speicher ist begrenzt und daher kostbar
im Gegensatz zum Mac / PC kann kein virtueller Speicher genutzt werden
Wie kann Speicher ausgehen?
Sven Günther http://www.it-agile.de 4
11. Speicherverwaltung
iPhone: 256MB RAM
Speicher ist begrenzt und daher kostbar
im Gegensatz zum Mac / PC kann kein virtueller Speicher genutzt werden
Wie kann Speicher ausgehen?
Leaks
Speicher auf den keine Referenz mehr besteht
Sven Günther http://www.it-agile.de 4
12. Speicherverwaltung
iPhone: 256MB RAM
Speicher ist begrenzt und daher kostbar
im Gegensatz zum Mac / PC kann kein virtueller Speicher genutzt werden
Wie kann Speicher ausgehen?
Leaks
Speicher auf den keine Referenz mehr besteht
Verlassener Speicher
Speicher der unnötig referenziert aber nicht mehr benutzt wird
Sven Günther http://www.it-agile.de 4
13. Leaks
Was sind Leaks?
Memory
MyClass *foo = [[MyClass alloc] init]; foo
0x0
Sven Günther http://www.it-agile.de 5
14. Leaks
Was sind Leaks?
Memory
MyClass *foo = [[MyClass alloc] init]; foo
foo = nil; // ==> Memory Leak 0x0
Sven Günther http://www.it-agile.de 5
15. Leaks finden
Sven Günther http://www.it-agile.de 6
16. iOS Reference Counting
in iOS keine Garbage Collection
aber
alle Objekte haben einen Referenzzähler (retainCount)
wenn der retainCount 0 erreicht so wird der Speicher freigegeben
Sven Günther http://www.it-agile.de 7
17. iOS Reference Counting
in iOS keine Garbage Collection
aber
alle Objekte haben einen Referenzzähler (retainCount)
wenn der retainCount 0 erreicht so wird der Speicher freigegeben
//->retainCount+1
alloc
new
retain
copy
Sven Günther http://www.it-agile.de 7
18. iOS Reference Counting
in iOS keine Garbage Collection
aber
alle Objekte haben einen Referenzzähler (retainCount)
wenn der retainCount 0 erreicht so wird der Speicher freigegeben
//->retainCount+1 //->retainCount-1
alloc release
*
new autorelease
retain
copy
Sven Günther http://www.it-agile.de 7
19. iOS Reference Counting
in iOS keine Garbage Collection
aber
alle Objekte haben einen Referenzzähler (retainCount)
wenn der retainCount 0 erreicht so wird der Speicher freigegeben
//->retainCount+1 //->retainCount-1
alloc release
*
new autorelease
retain immer paarweise
copy
Sven Günther http://www.it-agile.de 7
25. Object-Ownership
Wer ist verantwortlich dafür Objekte zu releasen?
Object-Owner
Wie wird man Object-Owner?
Bei Aufruf einer Methode,
die den retainCount erhöht.
Sven Günther http://www.it-agile.de 9
26. Object-Ownership
Wer ist verantwortlich dafür Objekte zu releasen?
Object-Owner
Wie wird man Object-Owner?
Bei Aufruf einer Methode,
die den retainCount erhöht.
Regel: Wer retained muss releasen!
Sven Günther http://www.it-agile.de 9
28. Factory-Methoden
Wie implementiere ich eine Factory-Methode?
+(Foo*) fooWithName:(NSString*) name {
Foo* [foo = [Foo alloc] init];
return [foo autorelease];
}
Sven Günther http://www.it-agile.de 10
29. Vorsicht bei Zuweisungen
Foo *foo = [[Foo alloc] init]; Memory
Foo *bar = [[Foo alloc] init];
foo
1
bar
1
Sven Günther http://www.it-agile.de 11
30. Vorsicht bei Zuweisungen
Foo *foo = [[Foo alloc] init]; Memory
Foo *bar = [[Foo alloc] init];
foo
1
foo = bar;
bar
1
Sven Günther http://www.it-agile.de 11
33. So ist es richtig
Foo *foo = [[Foo alloc] init]; Memory
Foo *bar = [[Foo alloc] init];
foo
1
bar
1
Sven Günther http://www.it-agile.de 12
34. So ist es richtig
Foo *foo = [[Foo alloc] init]; Memory
Foo *bar = [[Foo alloc] init];
foo
0
[foo release];
foo = [bar retain];
bar
2
1
Sven Günther http://www.it-agile.de 12
35. So ist es richtig
Foo *foo = [[Foo alloc] init]; Memory
Foo *bar = [[Foo alloc] init];
foo
0
[foo release];
foo = [bar retain];
bar
1
[foo release];
Sven Günther http://www.it-agile.de 12
36. So ist es richtig
Foo *foo = [[Foo alloc] init]; Memory
Foo *bar = [[Foo alloc] init];
foo
0
[foo release];
foo = [bar retain];
bar
0
[foo release];
[bar release];
Sven Günther http://www.it-agile.de 12
37. Implementierung von setter
// ivar Foo* foo;
-(void) setFoo:(Foo*) aFoo {
foo = aFoo;
}
Sven Günther http://www.it-agile.de 13
38. Implementierung von setter
// ivar Foo* foo;
-(void) setFoo:(Foo*) aFoo {
foo = aFoo; Leak foo
}
Sven Günther http://www.it-agile.de 13
39. Implementierung von setter
// ivar Foo* foo;
-(void) setFoo:(Foo*) aFoo {
foo = aFoo; Leak foo
}
aFoo nicht retained
Sven Günther http://www.it-agile.de 13
40. Implementierung von setter
// ivar Foo* foo;
-(void) setFoo:(Foo*) aFoo {
[foo release];
foo = [aFoo retain];
}
Sven Günther http://www.it-agile.de 14
41. Implementierung von setter
// ivar Foo* foo;
-(void) setFoo:(Foo*) aFoo {
[foo release]; Was ist wenn
foo = [aFoo retain]; aFoo == foo?
}
Sven Günther http://www.it-agile.de 14
42. Implementierung von setter
// ivar Foo* foo;
-(void) setFoo:(Foo*) aFoo {
if (foo != aFoo) {
[aFoo retain]; Regel:
[foo release];
foo = aFoo; erst retain dann release
}
}
Sven Günther http://www.it-agile.de 15
59. Master-Detail-Beziehungen
Übliches Pattern in iOS: Master - Detail
z.B. Ergebnisliste --> Detailansicht in Kontakten
Verantwortlichkeiten für Daten
Sven Günther http://www.it-agile.de 24
60. Master-Detail-Beziehungen
Übliches Pattern in iOS: Master - Detail
z.B. Ergebnisliste --> Detailansicht in Kontakten
Verantwortlichkeiten für Daten
MasterViewController erzeugt Daten für DetailViewController
Sven Günther http://www.it-agile.de 24
61. Master-Detail-Beziehungen
Übliches Pattern in iOS: Master - Detail
z.B. Ergebnisliste --> Detailansicht in Kontakten
Verantwortlichkeiten für Daten
MasterViewController erzeugt Daten für DetailViewController
MasterViewController initialisiert DetailViewController mit den Daten
Sven Günther http://www.it-agile.de 24
62. Master-Detail-Beziehungen
Übliches Pattern in iOS: Master - Detail
z.B. Ergebnisliste --> Detailansicht in Kontakten
Verantwortlichkeiten für Daten
MasterViewController erzeugt Daten für DetailViewController
MasterViewController initialisiert DetailViewController mit den Daten
DetailViewController zeigt Daten an
Sven Günther http://www.it-agile.de 24
63. Master-Detail-Beziehungen
Übliches Pattern in iOS: Master - Detail
z.B. Ergebnisliste --> Detailansicht in Kontakten
Verantwortlichkeiten für Daten
MasterViewController erzeugt Daten für DetailViewController
MasterViewController initialisiert DetailViewController mit den Daten
DetailViewController zeigt Daten an
Wenn DetailViewController releast wird, wird Speicher für DetailDaten freigegeben
Sven Günther http://www.it-agile.de 24
64. Globale Objekte
Objekte die von allen anderen Objekten benutzt werden
z.B. AppDelegate
Sven Günther http://www.it-agile.de 25
65. Globale Objekte
Objekte die von allen anderen Objekten benutzt werden
z.B. AppDelegate
nicht als Datencontainer missbrauchen!
Sven Günther http://www.it-agile.de 25
66. Crashes
Zugriff auf bereits freigegebenen Speicher
Threading mit UIKit / CoreData
Enumeration über mutable Collections
Sven Günther http://www.it-agile.de 26
67. Crashes bei Delegates
Foo *foo;
delegates werden i.d.R. nicht retained
foo.delegate = bar;
Sven Günther http://www.it-agile.de 27
68. Crashes bei Delegates
Foo *foo;
delegates werden i.d.R. nicht retained
foo.delegate = bar;
[foo release]
Vorsicht beim release des delegate
[bar release]; foo kann noch retained sein
Sven Günther http://www.it-agile.de 27
69. Crashes bei Delegates
Foo *foo;
delegates werden i.d.R. nicht retained
foo.delegate = bar;
[foo release]
Vorsicht beim release des delegate
[bar release]; foo kann noch retained sein
…
// Foo:
[delegate doSomething];
crash!
Sven Günther http://www.it-agile.de 27
70. Crashes bei Delegates
Foo *foo;
delegates werden i.d.R. nicht retained
foo.delegate = bar;
foo.delegate = nil;
[foo release]
Vorsicht beim release des delegate
[bar release]; foo kann noch retained sein
…
// Foo:
[delegate doSomething];
Sven Günther http://www.it-agile.de 27
71. Threading
Nicht alle Frameworks sind threadsafe
UIKit
Coredata
Sven Günther http://www.it-agile.de 28
72. UIKit
Änderungen an der UI immer nur im MainThread
[tableView reloadData] (nachdem Daten in Backgroundthread geladen wurden)
Sven Günther http://www.it-agile.de 29
73. UIKit
Änderungen an der UI immer nur im MainThread
[tableView reloadData] (nachdem Daten in Backgroundthread geladen wurden)
-(void) updateUI {
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(updateUI)
withObject:nil
waitUntilDone:NO];
return;
}
[tableView reloadData];
}
Sven Günther http://www.it-agile.de 29
74. CoreData
Änderungen an ManagedObjects nur im selben Thread durchführen, der das
Objekt erzeugt hat
insert in ManagedObjectContext
fetch from perstistent store
Sven Günther http://www.it-agile.de 30
75. Mutable Collections
NSMutableArray *strings;
...
for (NSString *s in strings) {
...
}
Sven Günther http://www.it-agile.de 31
76. Mutable Collections
NSMutableArray *strings;
...
NSArray *stringsCopy = [strings copy]; vor Enumeration Copy machen
for (NSString *s in stringsCopy) {
...
}
[stringsCopy release];
Sven Günther http://www.it-agile.de 31
79. Simulator == Device?
Speicher
Device 256MB (512MB iPad2)
Simulator unbegrenzt
CPU-Power
Simulator nutzt die volle Power des Host-Rechners
auf dem Device wird dann später die Performance nicht erreicht
Sven Günther http://www.it-agile.de 32
80. Simulator == Device?
Speicher
Device 256MB (512MB iPad2)
Simulator unbegrenzt
CPU-Power
Simulator nutzt die volle Power des Host-Rechners
auf dem Device wird dann später die Performance nicht erreicht
Unterschiedliche Implementierungen der SDK‘s
zB [UIImage imageNamed:nil] (z.B. im iOS SDK 4.1)
==> Simulator: gibt nil zurück
==> Device: Crash
Sven Günther http://www.it-agile.de 32
81. Simulator == Device?
Speicher
Device 256MB (512MB iPad2)
Simulator unbegrenzt
CPU-Power
Simulator nutzt die volle Power des Host-Rechners
auf dem Device wird dann später die Performance nicht erreicht
Unterschiedliche Implementierungen der SDK‘s
zB [UIImage imageNamed:nil] (z.B. im iOS SDK 4.1)
==> Simulator: gibt nil zurück
==> Device: Crash
Leak-Detection
==> Simulator: keine Leaks gefunden
==> Device: Instruments findet Leaks
Sven Günther http://www.it-agile.de 32