Wat betekenen atomic
en nonatomic
in de declaraties van eigenschappen?
@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;
Wat is het operationele verschil tussen deze drie?
Dit wordt uitgelegd in Apple's documentatie, maar hieronder staan enkele voorbeelden van wat er feitelijk gebeurt.
Merk op dat er geen "atomic" sleutelwoord is, als je niet "nonatomic" specificeert, dan is de eigenschap atomisch, maar het specificeren van "atomic" expliciet zal resulteren in een fout.
Als u niet "nonatomic" specificeert, dan is de eigenschap atomisch, maar u kunt nog steeds "atomic" expliciet specificeren in recente versies als u dat wilt.
//@property(nonatomic, retain) UITextField *userName;
//Generates roughly
- (UITextField *) userName {
return userName;
}
- (void) setUserName:(UITextField *)userName_ {
[userName_ retain];
[userName release];
userName = userName_;
}
Nu, de atomische variant is een beetje ingewikkelder:
//@property(retain) UITextField *userName;
//Generates roughly
- (UITextField *) userName {
UITextField *retval = nil;
@synchronized(self) {
retval = [[userName retain] autorelease];
}
return retval;
}
- (void) setUserName:(UITextField *)userName_ {
@synchronized(self) {
[userName_ retain];
[userName release];
userName = userName_;
}
}
In principe moet de atomische versie een slot nemen om de veiligheid van de thread te garanderen, en ook de ref count op het object verhogen (en de autorelease count om het in evenwicht te brengen) zodat het object gegarandeerd bestaat voor de aanroeper, anders is er een potentiële race conditie als een andere thread de waarde instelt, waardoor de ref count tot 0 daalt.
Er zijn eigenlijk een groot aantal verschillende varianten van hoe deze dingen werken, afhankelijk van of de eigenschappen scalaire waarden of objecten zijn, en hoe retain, copy, readonly, nonatomic, enz. interageren. In het algemeen weten de property synthesizers gewoon hoe ze het "juiste ding" moeten doen voor alle combinaties.
Atomic garandeert dat de toegang tot de eigenschap op een atomaire manier zal worden uitgevoerd. B.v. het retourneert altijd een volledig geïnitialiseerd object, elke get/set van een eigenschap op een thread moet voltooid zijn voordat een andere er toegang toe heeft.
Als je je voorstelt dat de volgende functie op twee threads tegelijk voorkomt, kun je zien waarom de resultaten niet mooi zouden zijn.
-(void) setName:(NSString*)string
{
if (name)
{
[name release];
// what happens if the second thread jumps in now !?
// name may be deleted, but our 'name' variable is still set!
name = nil;
}
...
}
Pros : Terugkeer van volledig geïnitialiseerde objecten elke keer maakt het de beste keuze in het geval van multi-threading.
Cons : Prestatieverlies, maakt de uitvoering een beetje trager
In tegenstelling tot Atomic, garandeert het niet dat een volledig geïnitialiseerd object elke keer terugkeert.
Pros : Extreem snelle uitvoering.
Cons : Kans op garbage value in geval van multi-threading.
Makkelijkste antwoord eerst: Er's geen verschil tussen je tweede twee voorbeelden. Standaard zijn property accessors atomair.
Atomische accessors in een niet-afgewikkelde omgeving (d.w.z. wanneer retain/release/autorelease wordt gebruikt) zullen een slot gebruiken om ervoor te zorgen dat een andere thread zich niet bemoeit met het correct instellen/verkrijgen van de waarde.
Zie de "Performance and Threading" sectie van Apple's Objective-C 2.0 documentatie voor wat meer informatie en voor andere overwegingen bij het maken van multi-threaded apps.