Dies ist eines dieser Dinge, in denen Cocoa all die schmutzigen Dinge hinter den Kulissen erledigt, und Sie wissen nie wirklich, wie kompliziert die Dinge sein können, bis Sie die Ärmel hochkrempeln und es selbst machen müssen.
Die einfache Antwort dafür, warum es nicht ‚einfach‘ ist, weil NSString
(und CFString
) befassen sich mit all den komplizierten Details mit mehrer Zeichensätzen, Unicode zu tun, etc, etc, während eine einfache, einheitliche API präsentieren Saiten zum Manipulieren . Es ist objektorientiert am besten - die Details von "wie" (NS|CF)String
geht auf Strings, die unterschiedliche String-Kodierungen haben (UTF8, MacRoman, UTF16, ISO 2022 Japanisch, etc.) ist ein privates Implementierungsdetail. Alles "funktioniert".
Es hilft zu verstehen, wie [@"..." UTF8String]
funktioniert. Dies ist ein Detail der privaten Implementierung, also ist dies kein Evangelium, sondern basiert auf beobachtetem Verhalten. Wenn Sie eine Zeichenfolge in der UTF8String
Nachricht senden, werden er die Zeichenfolge etwas annähert (nicht wirklich getestet, so halten es für Pseudo-Code, und es gibt tatsächlich einfache Möglichkeiten, die genau die gleiche Sache zu tun, so ist dies allzu ausführlich):
- (const char *)UTF8String
{
NSUInteger utf8Length = [self lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSMutableData *utf8Data = [NSMutableData dataWithLength:utf8Length + 1UL];
char *utf8Bytes = [utf8Data mutableBytes];
[self getBytes:utf8Bytes
maxLength:utf8Length
usedLength:NULL
encoding:NSUTF8StringEncoding
options:0UL
range:NSMakeRange(0UL, [self length])
remainingRange:NULL];
return(utf8Bytes);
}
Sie müssen sich keine Gedanken über die Speicherverwaltungsprobleme mit dem Puffer machen, den -UTF8String
zurückgibt, da die NSMutableData
automatisch freigegeben ist.
Ein String-Objekt ist frei, um den Inhalt der Zeichenfolge in der gewünschten Form zu halten. Daher gibt es keine Garantie dafür, dass die interne Darstellung die für Sie am besten geeignete ist (in diesem Fall UTF8). Wenn Sie nur C verwenden, müssen Sie mit der Verwaltung von Speicher umgehen, um eventuell erforderliche Zeichenfolgenkonvertierungen zu speichern. Was einmal ein einfacher Methodenaufruf war, ist jetzt viel, viel komplizierter.
meisten NSString
wird tatsächlich umgesetzt in/mit Corefoundation/CFString
, so gibt es offensichtlich einen Weg von einem CFStringRef
->-UTF8String
. Es ist einfach nicht so sauber und einfach wie NSString
-UTF8String
. Die größte Komplikation besteht in der Speicherverwaltung. Hier ist, wie ich es in der Vergangenheit in Angriff genommen habe:
void someFunction(void) {
CFStringRef cfString; // Assumes 'cfString' points to a (NS|CF)String.
const char *useUTF8StringPtr = NULL;
UInt8 *freeUTF8StringPtr = NULL;
CFIndex stringLength = CFStringGetLength(cfString), usedBytes = 0L;
if((useUTF8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8)) == NULL) {
if((freeUTF8StringPtr = malloc(stringLength + 1L)) != NULL) {
CFStringGetBytes(cfString, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', false, freeUTF8StringPtr, stringLength, &usedBytes);
freeUTF8StringPtr[usedBytes] = 0;
useUTF8StringPtr = (const char *)freeUTF8StringPtr;
}
}
long utf8Length = (long)((freeUTF8StringPtr != NULL) ? usedBytes : stringLength);
if(useUTF8StringPtr != NULL) {
// useUTF8StringPtr points to a NULL terminated UTF8 encoded string.
// utf8Length contains the length of the UTF8 string.
// ... do something with useUTF8StringPtr ...
}
if(freeUTF8StringPtr != NULL) { free(freeUTF8StringPtr); freeUTF8StringPtr = NULL; }
}
HINWEIS: Ich habe diesen Code nicht getestet, aber es wird modifiziert, von Code zu arbeiten. Abgesehen von offensichtlichen Fehlern glaube ich, dass es funktionieren sollte.
Das obige versucht, den Zeiger auf den Puffer zu erhalten, der CFString
verwendet, um den Inhalt der Zeichenfolge zu speichern. Wenn CFString
zufällig den Inhalt der Zeichenfolge in UTF8 codiert (oder eine entsprechend kompatible Codierung wie ASCII), dann wird wahrscheinlich CFStringGetCStringPtr()
nicht NULL
zurückgeben. Dies ist offensichtlich der beste und schnellste Fall. Wenn es den Zeiger aus irgendeinem Grund nicht bekommen kann, sagen wir, wenn CFString
seinen Inhalt in UTF16 kodiert hat, dann weist es einen Puffer mit malloc()
zu, der groß genug ist, um den gesamten String zu enthalten, wenn er nach UTF8 transcodiert wird. Dann, am Ende der Funktion, überprüft es, ob Speicher zugewiesen wurde und free()
ist es bei Bedarf.
Und jetzt für ein paar Tipps und Tricks ... CFString
"tendenziell" (und dies ist ein privates Implementierungsdetail, so dass es zwischen Releases ändern kann und ändert) halten Sie "einfache" Strings als MacRoman codiert, die ein ist 8-Bit breite Codierung. MacRoman ist wie UTF8 eine Obermenge von ASCII, so dass alle Zeichen < 128 äquivalent zu ihren ASCII-Gegenstücken sind (oder, mit anderen Worten, jedes Zeichen < 128 ist ASCII). In MacRoman sind Zeichen> = 128 Sonderzeichen. Sie haben alle Unicode-Entsprechungen und neigen dazu, Dinge wie zusätzliche Währungssymbole und "erweiterte westliche" Zeichen zu sein. Weitere Informationen finden Sie unter Wikipedia - MacRoman. Aber nur weil ein CFString
sagt, es ist MacRoman (CFString
Kodierungswert von kCFStringEncodingMacRoman
, NSString
Kodierungswert von NSMacOSRomanStringEncoding
) bedeutet nicht, dass es Zeichen> = 128 drin hat. Wenn eine kCFStringEncodingMacRoman
-codierte Zeichenfolge, die von CFStringGetCStringPtr()
zurückgegeben wird, vollständig aus Zeichen < 128 besteht, entspricht sie exakt der ASCII-codierten Darstellung (C), die ebenfalls exakt der Zeichenfolge UTF8 (kCFStringEncodingUTF8
) entspricht.
Je nach Ihren Anforderungen können Sie unter Verwendung von kCFStringEncodingMacRoman
anstelle von kCFStringEncodingUTF8
beim Aufruf CFStringGetCStringPtr()
"durchkommen". Die Dinge können (wahrscheinlich) schneller sein, wenn Sie eine strikte UTF8-Codierung für Ihre Zeichenfolgen benötigen, aber kCFStringEncodingMacRoman
verwenden und dann sicherstellen, dass die von CFStringGetCStringPtr(string, kCFStringEncodingMacRoman)
zurückgegebene Zeichenfolge nur Zeichen enthält, die < 128 sind. Wenn Zeichen> = 128 in der Zeichenfolge enthalten sind , dann gehen Sie die langsame Route von malloc()
in einem Puffer, um die konvertierten Ergebnisse zu halten. Beispiel:
CFIndex stringLength = CFStringGetLength(cfString), usedBytes = 0L;
useUTF8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8);
for(CFIndex idx = 0L; (useUTF8String != NULL) && (useUTF8String[idx] != 0); idx++) {
if(useUTF8String[idx] >= 128) { useUTF8String = NULL; }
}
if((useUTF8String == NULL) && ((freeUTF8StringPtr = malloc(stringLength + 1L)) != NULL)) {
CFStringGetBytes(cfString, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', false, freeUTF8StringPtr, stringLength, &usedBytes);
freeUTF8StringPtr[usedBytes] = 0;
useUTF8StringPtr = (const char *)freeUTF8StringPtr;
}
Wie ich schon sagte, Sie tun schätzen nicht wirklich, wie viel Arbeit Cocoa tut automatisch für Sie, bis Sie alles selbst zu tun haben.:)
Das ist eine Erklärung! Danke Johnne! Ich habe deinen Code ausprobiert und jetzt habe ich ein anderes Problem. Da ich mit ObjC in einer ".m" Datei gestartet habe, konnte ich schnell ein Beispiel erstellen. Nun, da ich zu C++ bin Umwandlung unter Verwendung einer ".mm" Datei I Ausnahmen auf Build erhalten: undefinierte Symbole: "___gxx_personality_v0", verwiesen von: ___ gxx_personality_v0 $ non_lazy_ptr in libMyNetworking.a (MyLowLevelNetworking.o) ld: Symbol (s) nicht gefunden Ich fühle mich immer noch so ahnungslos zu Apple Tools ... – Cliff