Đây là một trong những điều mà Cocoa làm tất cả những thứ lộn xộn đằng sau hậu trường, và bạn không bao giờ thực sự đánh giá cao những thứ phức tạp như thế nào cho đến khi bạn phải cuộn tay áo và tự làm.
Câu trả lời đơn giản vì sao không đơn giản là vì NSString
(và CFString
) xử lý tất cả các chi tiết phức tạp về xử lý nhiều bộ ký tự, Unicode, v.v. . Đó là đối tượng được định hướng tốt nhất- chi tiết về 'cách' (NS|CF)String
giải quyết các chuỗi có mã hóa chuỗi khác nhau (UTF8, MacRoman, UTF16, ISO 2022 Nhật Bản, v.v.) là chi tiết triển khai riêng tư. Tất cả 'chỉ hoạt động'.
Giúp hiểu cách hoạt động của [@"..." UTF8String]
. Đây là một chi tiết thực hiện riêng tư, vì vậy đây không phải là phúc âm, nhưng dựa trên hành vi được quan sát. Khi bạn gửi một chuỗi một thông báo UTF8String
, chuỗi sẽ làm điều gì đó gần đúng (không thực sự được kiểm tra, vì vậy hãy xem xét mã giả, và thực sự có những cách đơn giản hơn để làm điều tương tự, vì vậy điều này quá chi tiết):
- (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);
}
Bạn không phải lo lắng về các vấn đề quản lý bộ nhớ khi xử lý bộ đệm mà -UTF8String
trả về vì NSMutableData
được tự động phát hành.
Đối tượng chuỗi là miễn phí để giữ nội dung của chuỗi theo bất kỳ hình thức nào, vì vậy không đảm bảo rằng biểu diễn bên trong của nó là thuận tiện nhất cho nhu cầu của bạn (trong trường hợp này là UTF8). Nếu bạn đang sử dụng đồng bằng C, bạn sẽ phải đối phó với việc quản lý một số bộ nhớ để giữ bất kỳ chuyển đổi chuỗi nào có thể được yêu cầu. Những gì đã từng là một cuộc gọi phương thức -UTF8String
đơn giản bây giờ là nhiều, phức tạp hơn nhiều.
Hầu hết NSString
thực sự được triển khai trong/với CoreFoundation/CFString
, vì vậy rõ ràng là đường dẫn từ CFStringRef
->-UTF8String
. Nó không đơn giản và đơn giản như NSString
's -UTF8String
. Hầu hết các biến chứng là với quản lý bộ nhớ. Đây là cách tôi đã tức giải quyết nó trong quá khứ:
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; }
}
LƯU Ý: Tôi đã không kiểm tra mã này, nhưng nó được sửa đổi từ mã làm việc. Vì vậy, ngoài những lỗi rõ ràng, tôi tin rằng nó sẽ hoạt động.
Ở trên cố gắng đưa con trỏ đến bộ đệm CFString
sử dụng để lưu trữ nội dung của chuỗi. Nếu CFString
xảy ra có nội dung chuỗi được mã hóa bằng UTF8 (hoặc mã hóa tương thích thích hợp, chẳng hạn như ASCII), thì có khả năng CFStringGetCStringPtr()
sẽ trả về không NULL
. Đây rõ ràng là trường hợp tốt nhất và nhanh nhất. Nếu không thể nhận được con trỏ đó vì lý do nào đó, hãy nói rằng nếu CFString
có nội dung được mã hóa bằng UTF16, thì nó sẽ phân bổ bộ đệm với malloc()
đủ lớn để chứa toàn bộ chuỗi khi được chuyển mã sang UTF8. Sau đó, ở phần cuối của chức năng, nó sẽ kiểm tra xem bộ nhớ đã được cấp phát chưa và free()
nếu cần.
Và bây giờ để có một số mẹo và thủ thuật ... CFString
'có xu hướng' (và đây là chi tiết triển khai cá nhân, vì vậy nó có thể và thay đổi giữa các bản phát hành) giữ các chuỗi 'đơn giản' được mã hóa như MacRoman. Mã hóa rộng 8 bit. MacRoman, như UTF8, là một siêu của ASCII, sao cho tất cả các ký tự < 128 tương đương với các đối tác ASCII của chúng (hoặc nói cách khác, bất kỳ ký tự nào < 128 là ASCII). Trong MacRoman, ký tự> = 128 là ký tự 'đặc biệt'. Tất cả chúng đều có tương đương Unicode, và có xu hướng là những thứ như ký hiệu tiền tệ bổ sung và các ký tự 'mở rộng về phía tây'. Xem Wikipedia - MacRoman để biết thêm thông tin. Nhưng chỉ vì một số CFString
cho biết đó là MacRoman (CFString
giá trị mã hóa của kCFStringEncodingMacRoman
, NSString
giá trị mã hóa của NSMacOSRomanStringEncoding
) không có nghĩa là nó có ký tự> = 128 trong đó. Nếu một chuỗi được mã hóa kCFStringEncodingMacRoman
được trả về bởi CFStringGetCStringPtr()
được tạo thành hoàn toàn bằng các ký tự < 128, thì nó chính xác tương đương với biểu diễn được mã hóa ASCII (kCFStringEncodingASCII
), cũng chính xác tương đương với chuỗi biểu diễn được mã hóa UTF8 (kCFStringEncodingUTF8
).
Tùy thuộc vào yêu cầu của bạn, bạn có thể 'nhận được' bằng cách sử dụng kCFStringEncodingMacRoman
thay vì kCFStringEncodingUTF8
khi gọi CFStringGetCStringPtr()
. Mọi thứ 'có thể' (có thể) sẽ nhanh hơn nếu bạn yêu cầu mã hóa UTF8 nghiêm ngặt cho chuỗi của bạn nhưng sử dụng kCFStringEncodingMacRoman
, sau đó kiểm tra để đảm bảo chuỗi trả về bởi CFStringGetCStringPtr(string, kCFStringEncodingMacRoman)
chỉ chứa các ký tự là < 128. Nếu có ký tự> = 128 trong chuỗi , sau đó đi tuyến đường chậm bằng cách malloc()
nhập bộ đệm để giữ kết quả được chuyển đổi. Ví dụ:
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;
}
Như tôi đã nói, bạn không thực sự đánh giá cao công việc Cocoa tự động cho đến khi bạn phải tự làm tất cả.:)
Bây giờ đó là một lời giải thích! Thanx Johne! Tôi đã thử mã của bạn và bây giờ tôi có một vấn đề khác. Bởi vì tôi bắt đầu với ObjC trong một tập tin ".m" tôi đã có thể nhanh chóng giả lập một ví dụ. Bây giờ tôi đang chuyển sang C++ sử dụng một ".mm" tập tin tôi nhận được ngoại lệ về xây dựng: ký Undefined: "___gxx_personality_v0", tham chiếu từ: ___ gxx_personality_v0 $ non_lazy_ptr trong libMyNetworking.a (MyLowLevelNetworking.o) ld: biểu tượng (s) không tìm thấy Tôi vẫn cảm thấy thất vọng với các công cụ của Apple vào những thời điểm ... – Cliff