2010-01-19 7 views
7

Dies erzeugt ein unveränderliches String-Objekt:Cocoa: Testen, ob ein NSString unveränderlich oder veränderbar ist?

NSString* myStringA = @"A"; //CORRECTED FROM: NSMutableString* myStringA = @"A"; 

Dies erzeugt ein veränderliches String-Objekt:

NSMutableString* myStringB = [NSMutableString stringWithString:@"B"]; 

Aber beiden Objekte werden als die gleiche Art von Objekt berichtet, "NSCFString":

NSLog(@"myStringA is type: %@, myStringB is type: %@", 
[myStringA class], [myStringB class]); 

Also was unterscheidet diese Objekte intern, und wie teste ich das, damit ich leicht feststellen kann, ob eine Mystery-String-Variable immut ist fähig oder veränderlich, bevor Sie etwas Böses tun?

+0

Philippe-Code von - if ([myStringB isKindOfClass: [NSMutableString-Klasse]]) - funktioniert und löst das praktische Problem. – StringSection

+0

Ich bin immer noch neugierig darauf, wie der Unterschied zwischen einer unveränderlichen und veränderbaren Zeichenkette intern dargestellt wird, und ob das direkt erkannt werden kann (die tatsächliche "Objektart", die mit NSLog gedruckt wird). – StringSection

+1

Korrektur: Ich habe mich geirrt, der Code - if ([myStringB isKindOfClass: [NSMutableString Klasse]]) - funktioniert schließlich nicht. Es gibt True zurück, ob die Zeichenfolge NSString oder NSMutableString ist. Wie Philippe unten anführt (in seiner nun editierten Antwort mit anderem Code), gibt es offenbar keine dokumentierte Möglichkeit, veränderbare gegen unveränderliche Objekte zur Laufzeit allgemein zu erkennen. – StringSection

Antwort

4

Es gibt keine (dokumentierte) Möglichkeit festzustellen, ob eine Zeichenfolge zur Laufzeit veränderbar ist oder nicht.

Man würde erwarten, eine der folgenden Aktionen funktionieren würde, aber keiner von ihnen arbeiten:

[[s class] isKindOfClass:[NSMutableString class]]; // always returns false 
[s isMemberOfClass:[NSMutableString class]]; // always returns false 
[s respondsToSelector:@selector(appendString)]; // always returns true 

Mehr Infos hier, auch wenn es Ihnen nicht helfen, das Problem:

http://www.cocoabuilder.com/archive/cocoa/111173-mutability.html

+0

Funktioniert nicht: NSMutableString * lala = @ "lala"; \t if ([lala isKindOfClass: [NSMutableString-Klasse]]) NSLog (@ "Mutable-Klasse"); - zeigt log message, jedoch Objekt ist unveränderlich – Vladimir

+0

Natürlich funktioniert es nicht, weil der Code falsch ist. ** NSMutableString * lala = @ "lala" ** ist illegaler Code. –

+0

Sorry Leute - NSMutableString * myStringA = @ "A"; war ein Tippfehler - jetzt in der ursprünglichen Nachricht korrigiert. – StringSection

3

Wenn Sie nach Debugging-Zwecken überprüfen möchten, sollte der folgende Code funktionieren. Kopieren auf unveränderliche Objekt ist selbst, während es eine echte Kopie für veränderbare Typen ist, das ist, worauf der Code basiert. Beachten Sie, dass es langsam ist, da es copy aufruft, aber für das Debuggen in Ordnung sein sollte. Wenn du nach anderen Gründen als dem Debuggen suchen möchtest, siehe Rob antwort (und vergiss es).

Haftungsausschluss: Natürlich gibt es keine Garantie, dass die Kopie gleichbedeutend mit behalten für unveränderliche Arten ist. Sie können sicher sein, dass, wenn isMutable NEIN zurückgibt, es nicht veränderbar ist, also sollte die Funktion wahrscheinlich canBeMutable heißen. In der realen Welt ist es jedoch eine ziemlich sichere Annahme, dass unveränderliche Typen (NSString, NSArray) diese Optimierung implementieren. Es gibt eine Menge Code out einschließlich grundlegende Dinge wie NSDictionary, die schnelle Kopie von unveränderlichen Typen erwartet.

12

Die Dokumentation enthält eine ziemlich lange Erklärung, warum Apple dies nicht möchte und warum sie dies explizit in Receiving Mutable Objects nicht unterstützt. Die Zusammenfassung ist:

So stellen keine Entscheidung über Objekt Veränderlichkeit basiert auf, was Selbstbeobachtung informiert Sie über ein Objekt. Behandeln Sie Objekte als veränderbar oder nicht basierend auf , was Sie in der API Grenzen (das heißt, basierend auf der Rückgabetyp) übergeben werden. Wenn Sie ein Objekt eindeutig als veränderlich oder unveränderlich markieren müssen, wenn Sie es an Clients übergeben, übergeben Sie diese Information als Flag zusammen mit dem Objekt.

Ich finde ihre NSView-Beispiel am einfachsten zu verstehen, und es veranschaulicht ein grundlegendes Cocoa-Problem. Sie haben ein NSMutableArray mit dem Namen "elements", das Sie als Array bereitstellen möchten, aber nicht möchten, dass Anrufer sich darum kümmern. Sie haben mehrere Optionen:

  1. Legen Sie Ihr NSMutableArray als NSArray offen.
  2. Erstellen Sie immer eine nicht veränderbare Kopie, wenn Sie dazu aufgefordert werden
  3. Speichern Sie Elemente als NSArray und erstellen Sie jedes Mal ein neues Array, wenn es mutiert.

Ich habe all diese an verschiedenen Punkten getan. # 1 ist bei weitem die einfachste und schnellste Lösung. Es ist auch gefährlich, da das Array hinter dem Rücken des Anrufers mutieren könnte. Aber Apple gibt in einigen Fällen an, was sie tun (beachten Sie die Warnung für -subviews in NSView). Ich kann bestätigen, dass, während # 2 und # 3 viel sicherer sind, sie große Leistungsprobleme verursachen können, weshalb Apple sich entschieden hat, sie nicht für häufig verwendete Mitglieder wie -subviews zu verwenden.

Das Ergebnis von all dem ist, dass, wenn Sie # 1 verwenden, die Introspektion Sie in die Irre führen wird. Sie haben eine NSMutableArray-Darstellung als NSArray, und die Introspektion zeigt an, dass sie veränderbar ist (Introspektion hat keine Möglichkeit, etwas anderes zu erfahren). Aber du darfst es nicht mutieren. Nur die Kompilierzeitprüfung kann Ihnen das sagen, und das ist das einzige, dem Sie vertrauen können.

Der Fix für diese wäre eine Art schnelle Kopie-auf-schreiben unveränderliche Version einer veränderbaren Datenstruktur. Auf diese Weise könnte # 2 möglicherweise mit anständiger Leistung durchgeführt werden. Ich kann mir vorstellen, dass Änderungen am NSArray-Cluster dies erlauben würden, aber es existiert heute in Cocoa nicht (und könnte die NSArray-Leistung im Normalfall beeinträchtigen, was es zu einem Nicht-Starter macht). Selbst wenn wir es hätten, gibt es wahrscheinlich zu viel Code, der darauf angewiesen ist, dass das aktuelle Verhalten Mutabilität introspizieren kann.

+1

Sie können das Leistungsproblem mit # 3 lösen, indem Sie mutierende Array-Zugriffsmethoden implementieren und verfügbar machen (http://developer.apple.com/documentation/Cocoa/Conceptual/ModelObjects/Articles/moAccessorMethods.html) und sie in anderen Klassen verwenden. Auf diese Weise bleibt der direkte Zugriff auf das veränderbare Array privat, aber Sie erstellen und löschen keine temporären Arrays. –

+1

@PH Das stimmt und ich hätte die KVC-Lösung abdecken sollen. Das Problem besteht darin, dass es viele Fälle gibt, in denen der Aufrufer wirklich eine Sammlung benötigt (normalerweise, um zu etwas anderem zu gelangen, das einen verlangt). Ich habe das oft dadurch gelöst, dass ich etwas wie elementEnumerator offengelegt habe, das der Anrufer bei Bedarf zu einem Schnappschuss machen kann. Das Problem bei diesem Ansatz ist die Menge an zusätzlichem Code, der sowohl für den Anrufer als auch für den Angerufenen erforderlich ist. Es ist aufschlussreich, dass Apple selbst im neuen Code für UIView entschied, die "Subviews" von NSMutableArray nur als NSArray verfügbar zu machen und keine vollständige KVC bereitzustellen. –

+0

Hm? Mir ist nicht klar, über welches Problem du sprichst oder was deine Lösung ist. Blogpost und/oder Pastebin? –

Verwandte Themen