Wie synchronisiert @synchronized in Objective-C?

Verwendet @synchronized nicht “lock” und “unlock”, um gegenseitigen Ausschluss zu erreichen? Wie macht es dann sperren / entsperren?

Die Ausgabe des folgenden Programms ist nur “Hello World”.

@interface MyLock: NSLock @end @implementation MyLock - (id)init { return [super init]; } - (void)lock { NSLog(@"before lock"); [super lock]; NSLog(@"after lock"); } - (void)unlock { NSLog(@"before unlock"); [super unlock]; NSLog(@"after unlock"); } @end int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; MyLock *lock = [[MyLock new] autorelease]; @synchronized(lock) { NSLog(@"Hello World"); } [pool drain]; } 

   

Die Synchronisation der Objective-C-Sprache verwendet den Mutex, genau wie NSLock . Semantisch gibt es einige kleine technische Unterschiede, aber es ist grundsätzlich richtig, sie als zwei separate Schnittstellen zu betrachten, die auf einer gemeinsamen (primitiveren) Entität implementiert sind.

Insbesondere bei einer NSLock Sie eine explizite Sperre, während Sie bei @synchronized eine implizite Sperre haben, die dem Objekt zugeordnet ist, das Sie zum Synchronisieren verwenden. Der Vorteil der Sperrung auf Sprachenebene besteht darin, dass der Compiler sie versteht, so dass sie Probleme mit dem Gültigkeitsbereich lösen kann, sich aber mechanisch im Wesentlichen gleich verhält.

Sie können sich @synchronized als Compiler @synchronized :

 - (NSString *)myString { @synchronized(self) { return [[myString retain] autorelease]; } } 

wird umgewandelt in:

 - (NSString *)myString { NSString *retval = nil; pthread_mutex_t *self_mutex = LOOK_UP_MUTEX(self); pthread_mutex_lock(self_mutex); retval = [[myString retain] autorelease]; pthread_mutex_unlock(self_mutex); return retval; } 

Das ist nicht genau richtig, weil die tatsächliche Transformation komplexer ist und rekursive Sperren verwendet, aber es sollte den Punkt über erhalten.

In Objective-C behandelt ein @synchronized Block das Sperren und Entsperren (sowie mögliche Ausnahmen) automatisch für Sie. Die Laufzeit generiert im Wesentlichen dynamisch eine NSRecursiveLock, die dem Objekt zugeordnet ist, auf dem Sie synchronisieren. Diese Apple-Dokumentation erklärt es genauer. Aus diesem Grund sehen Sie keine Protokollmeldungen von Ihrer NSLock-Unterklasse – das Objekt, mit dem Sie synchronisieren, kann alles sein, nicht nur ein NSLock.

Im Grunde ist @synchronized (...) ein Convenience-Konstrukt, das Ihren Code rationalisiert. Wie die meisten vereinfachenden Abstraktionen, hat es Overhead (denken Sie an es als versteckte Kosten), und es ist gut, sich dessen bewusst zu sein, aber rohe performance ist wahrscheinlich nicht das höchste Ziel, wenn solche Konstrukte trotzdem verwendet werden.

Tatsächlich

 { @synchronized(self) { return [[myString retain] autorelease]; } } 

verwandelt sich direkt in:

 // needs #import  { objc_sync_enter(self) id retVal = [[myString retain] autorelease]; objc_sync_exit(self); return retVal; } 

Diese API ist seit iOS 2.0 verfügbar und wird mit … importiert.

 #import  

Apples Implementierung von @synchronized ist Open Source und kann hier gefunden werden . Mike Ash hat zwei wirklich interessante Beiträge zu diesem Thema geschrieben:

  • Schlösser, Fadensicherheit und Swift
  • Lass uns @synchronisiert bauen

pthread_mutex_t gesagt verfügt es über eine Tabelle, die Objektzeiger (die ihre Speicheradressen als Schlüssel verwenden) den pthread_mutex_t Sperren pthread_mutex_t , die je nach pthread_mutex_t gesperrt und entsperrt werden.

Es verbindet nur ein Semaphor mit jedem Objekt und benutzt dieses.