m (→ARC) |
m (minor update) |
||
(2 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
WICHTIG: Diese Seite handelt von mittlerweile mehr oder weniger obsolet gewordenem Memory Management. | |||
Unter Punkt 6. ARC wird die aktuelle Vorgehensweise beschrieben! | |||
== Retain-Count == | == Retain-Count == | ||
Line 160: | Line 164: | ||
* Weniger Code | * Weniger Code | ||
* Der Programmierer kann sich auf das Wesentliche konzentrieren | * Der Programmierer kann sich auf das Wesentliche konzentrieren | ||
=== Beispiel Deklaration === | === Beispiel Deklaration === | ||
Line 177: | Line 182: | ||
<source lang="ObjC">MyObject : NSObject { | <source lang="ObjC">MyObject : NSObject { | ||
NSString __strong *myName; | NSString __strong *myName; | ||
id<MyProtocol> | id<MyProtocol> delegate; | ||
int myNumber; | int myNumber; | ||
} | } | ||
@property(nonatomic, strong) NSString* myName; | @property(nonatomic, strong) NSString* myName; | ||
@property(nonatomic, | @property(nonatomic, assign) id<MyProtocol> delegate; | ||
@property(nonatomic) int myNumber; | |||
</source> | |||
=== Aktuelles Beispiel (März 2013) === | |||
...oder noch einfacher: | |||
(und im Augenblick die empfohlene Vorgehensweise) | |||
<source lang="ObjC">MyObject : NSObject { } | |||
@property(nonatomic, strong) NSString* myName; | |||
@property(nonatomic, assign) id<MyProtocol> delegate; | |||
@property(nonatomic) int myNumber; | @property(nonatomic) int myNumber; | ||
</source> | </source> | ||
Bei letzterem Beispiel werden alle Instanzvariablen durch Properties ersetzt. D.h. man muss sie mit self. ansprechen (also self.myNumber anstatt myNumber). | |||
Line 210: | Line 229: | ||
* http://www.learn-cocos2d.com/2011/11/everything-know-about-arc/ | * http://www.learn-cocos2d.com/2011/11/everything-know-about-arc/ | ||
== Links == | == Links == | ||
Line 217: | Line 236: | ||
{{Template:iPhoneDev}} | {{Template:iPhoneDev}} | ||
[[Category:Added ARC]] |
Latest revision as of 15:02, 12 March 2013
WICHTIG: Diese Seite handelt von mittlerweile mehr oder weniger obsolet gewordenem Memory Management. Unter Punkt 6. ARC wird die aktuelle Vorgehensweise beschrieben!
Retain-Count
Das Memory-Management von Cocoa basiert auf einem Retain-Count Mechanismus. Anders als z.B. bei C (malloc & free), gibt es in Objective-C eine klar geregelte Verantwortung für den Lebenszyklus des Objektes:
Ownership
- Wer das Objekt erstellt (also mit alloc Speicher dafür reserviert) =>
- ist der "Creator" und muss die Verantwortung für die Speicherfreigabe übernehmen (also mit release den Speicher wieder freigeben).
Jedes Objekt hat einen sog. retain count, der normalerweise bei der Erstellung des Objektes (z.B. mit alloc oder copy) einen Wert von 1 hat. Wenn nun dieses Objekt von einem anderen Objekt als seinem Eigentümer für längere Berechnungen oder als eigene Instanz-Variable benötigt wird, so kann man [obj retain] aufrufen. Damit steigt der retain-count dieses Objektes um 1 auf 2. Das Objekt wird gelöscht (bzw. der [obj dealloc] aufgerufen), wenn der retain-count auf 0 zurück geht.
Wichtig:
Der Programmierer muss dafür sorgen, dass seine retain/release bzw. alloc/release bzw. copy/release Aufrufe ausgeglichen sind. Ansonsten kommt es zu Leaks (Zombie-Objekte) oder zu einem Crash, wenn ein Objekt per release freigegeben wird, das es nicht mehr gibt.
Folgende Aufrufe erfordern einen späteren release, weil sie den retain-count um jeweils +1 erhöhen:
- alloc (owner)
- copy (owner)
- new (owner, selten verwendet)
- retain
Autorelease erhöht zwar den retain-count ebenfalls +1, in der nächsten Runloop wird der retain-count allerdings automatisch um −1 verringert. Deshalb ist hierfür kein release mehr erforderlich.
Beispiel
Rechenbeispiel zum retain-count (kein wirklich sinnvolles Code-Beispiel!)
NSString *myObj; // retaincount: 0
myObj = [[NSString alloc] initWithString:@"hello"]; // retaincount: 1
[myObj retain]; // retaincount: 2
[myObj release]; // retaincount: 1
NSString *newString = [myObj copy]; // retaincount myObj: unverändert (1), newString: 1
[newString autorelease]; // retaincount: 1; next loop retaincount: 0
[myObj release]; // retaincount: 0
Alloc/Init/Copy/Retain
NSString *newString = [[NSString alloc] init];
NSString *anotherString = [newString copy];
NSString *aThirdString = [anotherString retain];
- alloc (allocate) reservier Speicher für ein Objekt (und macht sonst nichts weiter, deshalb sieht man alloc immer nur in Verbindung mit init: [[obj alloc] init] oder [[obj alloc] initWithSomething:...]
- alloc erstellt ein Objekt mit einem anfänglichen retain-count von 1
- init (initialize) initialisiert das Objekt
- init erhöht den retain-count nicht!
- copy kopiert das Objekt und erstellt ein neues, eigenständiges Objekt mit einem eigenen Speicherbereich
- copy erstellt ein Objekt mit einem anfänglichen retain-count von 1
- retain sorgt dafür, dass das Objekt auf jeden Fall erhalten bleibt. Es wird kein neues Objekt erstellt.
- retain erhöht den retain-count um 1.
- autorelease sendet ein release in der nächsten Run-Loop. Der retain-count bleibt im aktuellen Run-Loop erhalten.
- autorelease verringert den retain-count um 1 in der nächsten Programmschleife
Autorelease
Die Run Loop wird mit dem Start des Programms erstellt. Objekte, die eine autorelease-Nachricht erhalten, werden in den sog. Autorelease-Pool aufgenommen und warten auf die nächste Schleife. Erst zu Beginn der nächsten Run-Loop erhalten die Objekte im Pool eine release-Nachricht und werden aus dem Pool entfernt. Damit wird sichergestellt, dass das Objekt während der aktuellen Schleife noch vorhanden und gültig ist. Innerhalb einer Runloop-Schleife sind wir z.B. wenn ein Event (Mausklick) erfolgt ist.
Autorelease-Pool Funktionsweise
Bildnachweis: © basiert auf Stanford iPhone Application Programming 2009 (iTunes U), Slide from Lecture 3
Beispiel
Interface (file.h)
@interface MyClass:NSObject {
NSString *name;
int age;
id delegate;
}
@property (nonatomic, retain) id delegate;
// Hinweis: name und age würden normalerweise auch einfacher als properties verwendet!
-(NSString*)name;
-(void)setNewName:(NSString*)newName;
-(int)age;
-(void)setAge:(int)newAge;
@end
Implementation (file.m)
#import "file.h"
@implementation MyClass
@synthesize delegate;
-(id)init {
self = [super init];
if(self) {
[self setName:@"Barnie"];
[self setAge:30];
}
}
-(NSString*)name { return name; }
-(void)setName:(NSString*)newName {
if(newName != name) {
[name release];
[newName retain];
name = newName;
}
}
-(int)age { return age; }
-(void)setAge:(int)newAge { age = newAge; }
-(void)dealloc {
[delegate release]; // retain-property!
[self setName:nil]; // release wird im Setter gesendet!
// age braucht keinen release weil int ist kein Objekt!
}
@end
Fragen:
- was ist ein Accessor?
- was passiert im dealloc wenn man schreibt: name = nil;?
- wo ist der Unterschied:
- [self setName:nil];
- self.name = nil;
- name = nil;
Regeln
- Genau geregelte Verantwortlichkeiten:
- Wer alloc, new, copy, retain verwendet, muss auch jeweils 1x release senden
- Wer nicht alloc, new, copy, retain aufruft, darf nicht release aufrufen
- Niemals dealloc selbst aufrufen (es sei denn man überschreibt die Methode und ruft [super dealloc] auf!
ARC
Was ist ARC
Seit dem LLVM 3.0 Compiler (enthalten im iOS 5 SDK) gibt es Automated Reference Counting, das auf Compiler-Ebene automatisiert retain-/release calls einfügt. Demnach müssen alle obigen Regeln eigentlich nicht mehr beachtet werden, was eine unglaubliche Erleichterung für den Programmierer darstellt. Die Kenntnis über die Speicherverwaltung ist zwar noch von Vorteil, aber man spart sehr viele Zeilen Code und die Gefahr eines Programmabsturzes wird deutlich geringer.
Arc besteht im Prinzip aus nur wenigen Schlüsselwörtern. Bis auf __weak sind dabei alle schon in ObjC vorhanden und daher abwärtskompatibel:
- __strong
- __weak (>iOS5!)
- __unsafe_unretained
- __bridge ( inkl. __bridge_retained und __bridge_transfer)
- __autoreleasing (@autorelease)
Vorteile
- ARC ist kein Garage Collector, es passiert nichts zur Laufzeit
- Der Code wird auf Compiler-Ebene optimiert und läuft daher schneller als vorher!
- ARC ist zu iOS4 und Snow Leopard 10.6 abwärtskompatibel
- Crash-Safe: deutlich weniger Abstürze wegen Speicherproblemen
- Weniger Code
- Der Programmierer kann sich auf das Wesentliche konzentrieren
Beispiel Deklaration
So wird aus folgendem Beispiel...
MyObject : NSObject {
NSString *myName;
id<MyProtocol> delegate;
int myNumber;
}
@property(nonatomic, retain) NSString* myName;
@property(nonatomic, assign) id<MyProtocol> delegate;
@property(nonatomic) int myNumber;
...dieser Code mit ARC:
MyObject : NSObject {
NSString __strong *myName;
id<MyProtocol> delegate;
int myNumber;
}
@property(nonatomic, strong) NSString* myName;
@property(nonatomic, assign) id<MyProtocol> delegate;
@property(nonatomic) int myNumber;
Aktuelles Beispiel (März 2013)
...oder noch einfacher: (und im Augenblick die empfohlene Vorgehensweise)
MyObject : NSObject { }
@property(nonatomic, strong) NSString* myName;
@property(nonatomic, assign) id<MyProtocol> delegate;
@property(nonatomic) int myNumber;
Bei letzterem Beispiel werden alle Instanzvariablen durch Properties ersetzt. D.h. man muss sie mit self. ansprechen (also self.myNumber anstatt myNumber).
Beispiel no more retain/release
Mit ARC gibt es nun einfach kein retain mehr; dies ist mustergültiger Code:
id myObj = [[MyClass alloc] init];
self.myProperty = myObj;
// no more [myObj release];
return;
Auch entfallen manche Methoden komplett, es gibt keinen Grund mehr -(void)dealloc zu überschreiben.
Voraussetzungen
Mindest-Voraussetzungen zum Einsatz von ARC:
- Apple LLVM compiler 3.0
- enhalten im iOS 5 SDK bzw. Mac OS X 10.5. SDK mit Xcode 4
- Bei bestehenden Projekten kann der Refactor-Assistent zum Migrieren genutzt werden (Achtung: u.U. muss das .git Verzeichnis temporär verschoben werden)
Darüber hinaus ist ARC zu iOS 4.x abwärtskompatibel, wenn man:
- kein __weak benutzt sondern __unsafe_unretained
Siehe auch:
Links
- Memory Management Programming Guide for Cocoa (Hinweis: iOS bis mind. 4.x hat keine Garbage Collection!)
Diese Seite ist Teil des Werkmoduls iOS Development von Michael Markert für Interface Design / Fakultät Medien an der Bauhaus-Universität Weimar.