字符串对象给你许多可变的方法去创建CFString对象-从字符串常量,从缓冲区,从格式化的字符串,使用已经存在的CFString对象。下面的章节描述了所有的这些技巧。
一些函数返回CFString对象的引用将在别的文章中描述。CFStringCreateWithBytes函数在“字符串编码转换(32页)”。在“处理字符串外部描述”中描述了CFStringCreat
FromExternalRepresentation函数。
(1)从字符串常量中创建CFString对象
最简单的方式创建不可变的CFString对象是用CFSTR宏命令。这个宏的参数必须是一个编译时常量字符串,在一对括号中结束的字符串。CFSTR返回一个CFString对象
的引用。
这里有一个例子:
CFStringRef hello = CFSTR("hello,world");
返回的字符串有以下语义:
1.因为CFSTR不是一个创建和拷贝函数,当你不再需要使用它的时候你无需释放字符串对象所有权。
2.字符串不用CFString释放。换句话说,CFString保证它是有效的知道程序结束。
3.这个字符串不可以被释放或者保留以这种方式,像其他CFString一样。
如果在可执行文件中有两个或者说是多个实例,在某些情况下可能只有一个被保存起来。一个常用的CFSTR用途就是创建格式化的字符串(参见“从格式化的字符串中创建字符串对象”以获取更多信息)。
(2)从字符串缓冲区中创建CFString对象
一个创建CFString常用的方法是调用带有C字符缓冲区(或者说是字符串指针)函数作为“源”对于这个对象。这些函数是转换CFString对象到C字符串的函数副本;参见“访问字符串对象的内容”以获取更多信息。
这些函数分为两类。一部分函数创建的CFString对象复制缓冲区到内部存储中,你一旦创建了这个对象,你可以自由的处理缓冲区。关联的函数从C字符串缓冲区(CFStringCreateWithCSring)创建CFString对象,或者从Unicode字符串缓冲区(CFStringCreateWithCharacters)。另一些函数带一个另外的字符数的参数但是不包含编码参数。
另一部分函数带有一致的名字以NoCopy结尾。这些函数从一个用户提供的字符串缓冲区但是不复制缓冲区到对象的内部存储创建了CFString对象。他们尝试但不保证所提供的指针是使用缓冲区像后备存储一样而不用拷贝数据。当然你需要确认你不能释放缓冲区当CFString存在的时候。字符串数据应当不在栈区或者是有生命周期的数据当你不能保证时。
实际上,这些NoCopy函数在一个有限数量的环境中可以使用:
1.你有编译时常量数据例如一个C字符串(“Hello”)。NoCopy函数提供一个有效的方法从这些数据使CFString对象,如果你指定kCFAllocatorNull作为最后一个参数,当CFString不在存在时,缓冲区将不会被自动释放。(你也可以使用CFSTR宏来实现相同的目的)。
2.你为一些字符串数据分配了一些内存,你想将一个CFString对象放进内存,但是有时候你不需要原来的内存。当然你可以使用non-NoCopy函数(会复制数据)创建一个CFString对象,然后释放缓冲区,但是NoCopy函数可以让你去转换内存所有权给CFString对象,在你所需时释放内存。
NoCopy函数包括一个额外的参数(contentsDeallocator),该参数是传递字符串引用到一个用于释放缓冲区当不需要的时候CFAllocator对象。如果默认CFAllocaor对象足以达到此目的,你可以使用NULL。如果你不想CFString去释放缓冲区,传递kCFAllocatorNull。
列表1 表示用CFStringCreateWithCStringNoCopy函数CFString对象创建
列表1 用NoCopy函数创建CFString对象
const char *bytes;
CFStringRef str;
bytes = CFAllocatorAllocate(CFAllocatorGetDefault(),6,0);
strcpy(bytes,"hello");
str=CFStringCreateWithCStringNoCopy(NULL,bytes,kCFStringEncodingMacRoman,NULL);
CFRelease(str);/*默认内存分配一样需要释放bytes内存*/
重要:使用NoCopy函数创建CFString对象不一定需要使用你所提供的缓冲区。在一些情况下这个对象可能使用其他缓冲区而已经释放该缓冲区,例如,它可能需要决定内部使用Unicode编码。这种行为可能改变内存释放。
你可以使用你完全控制源的缓冲区创建可变CFString对象参见“使用客户端拥有的缓冲区创建可变字符串”。
(3)从格式化的字符串创建字符串对象
字符串对象包含从格式化字符串创建CFString对象的函数-字符串合并printf样式的说明符以达到代入变量的值到字符串中,然后转换他们(如果需要)到字符数据。字符串格式化说明符在“字符串格式化界定符”中定义。当需要显示可变元素的字符串信息的时候格式化字符串是有用的。举个例子,当你建立一个对话框去显示一个操作的进度(例如“拷贝文件y中的x文件”)你可能需要使用这些函数。
CFStringCreateWithFormat函数从一个简单格式化字符串创建CFString对象,如列表2中所示。
列表2 从一个格式化字符串中创建一个CFString
CFStringRef printGross(CFStringRef employeeName,UInt8 hours,float wage)
{
return CFStringCreateWithFormat(NULL, NULL, CFSTR("Employee %@ earned $%.2f this week."),employeeName,hours*wage);
}
第一个参数,像通常一样,指定需要使用的内存分配对象(NULL意义使用默认的CFAllocator对象)。第二个参数时本地依赖格式化选项,例如十进制和十六进制分隔符;现在还没有使用。剩下的参数时格式化的字符串和变量的值。
跟之前提到的一样,格式化字符串有printf样式说明符嵌入到其中(举个例子,"%d,%s,%2,2f")。Core Foundation对此转化介绍了一对扩展。其中一个是%@说明符(在列表2中),它表示任何Core Foundation对象。另一个新的说明符表示参数顺序。这个说明符携带n$,n时接下来字符串的顺序数。当你想本地化整个场景甚至是段落到其他语言时而不需要担心这些参数的顺序(在一种语言到另外一种时可能会变化)这个参数顺序(argument-order)功能是有用的。
举个例子,上面的这个函数可能会产生一个结果字符串“John Doe earned %1012.32 this week。”但是在其他语言中相同的情况从表达的语法上(大致翻译)可能是“¥1012.32 was earned by John Doe this week.”你不需要使用不同的参数书序去重新调用CFStringCreateWithFormat。你只需要调用这个函数,如下所示:
return CFStringCreateWithFormat(NULL,NULL,CFSTR("$%2$.2f was earned by employee %1$@."),employeeName,hours*wage);
当然,字符串对象自身不能硬编码(hard-coded),可能会从包含本地化字符串和他们的翻译的文件加载(举个例子,一个xml属性列表或者一个公开的ASCII属性列表)
另一个CFString对象,CFStringCreateWithFormatAndArguments,可以和一个格式化字符串一起携带变量参数列表(vararg)。这个函数允许格式化参数列表到函数中,用法参考列表3:
列表3 从一个变量参数列表中创建CFString
void show(CFStringRef formatString,...)
{
CFStringRef resultString;
CFDataRef data;
va_list argList;
va_start(argList, formatString);
resultString = CFStringCreateWithFormatAndArguments(NULL, NULL, formatString, argList);
va_end(argList);
data = CFStringCreateExternalRepresentation(NULL, resultString, kCFStringEncodingMacRoman, '?');
if (data != NULL) {
printf("%.*s\n\n",(int)CFDataGetLength(data),CFDataGetBytePtr(data));
CFRelease(data);
}
CFRelease(resultString);
}
//本人测试结果,传递三个CFStringRef对象到show中,打印只显示第一个参数,但是用va_arg遍历参数表参数已经传递到show()函数中,不知道为什么?欢迎交流。
(4)创建可变的字符串对象
字符串对象仅包含一小部分创建可变CFString对象的函数。至于为什么时一小部分这个原因是显而易见的。因为是可变对象,在你创建后使用“操作可变字符串”描述中的函数修改他们。
这里有两个基本的可变CFString对象创建函数。CFStringCreateMutable函数创建一个空对象;CFStringCreateMutableCopy从一个不可变的CFString对象创建一个可变的副本。列表4阐述了后一个函数,显示如何将一个字符追加到创建的对象当中。
列表4 创建一个CFString对象的可变副本
const UniChar u[] = {'5','+','*','d','x','4','Q','?'};
CFMutableStringRef str;
CFDataRef data;
str = CFStringCreateMutableCopy(NULL, 0, CFSTR("abc"));
CFStringAppendCharacters(str, &u[1], 1);//CFStringAppendCharacters(str, u, 8);
data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, str, kCFStringEncodingMacRoman, '?');
if (data != NULL) {
printf("%.*s\n\n",(int)CFDataGetLength(data),CFDataGetBytePtr(data));
CFRelease(data);
}
CFRelease(str);
这个两个函数的第二个参数时CFIndex值叫做maxLength。这个值指定在这个字符创中的最大数量的字符和创建对象去优化存储和捕获错误(如果太多的字符串插入)。如果指定0(向上面的列表4中)表示这个字符串可以到任何大小。
(5)使用客户端拥有缓冲区(Client-Owned Buffers)创建可变字符串
当你创建大部分核心基础的对象,这个对象拥有你提供的初始化数据和在内部存储数据。字符串对象对于这种行为允许一些例外,对于可变CFString对象这个例外是:CFStringCreateMutableWithExternalCharactersNoCopy函数。这个函数创建一个后备存储是Unicode缓冲区(你自己创建的并且拥有)可变CFString对象。你可以独立于这个对象测试和操纵这个缓冲区。
列表 5 创建一个独立后备存储的可变CFString对象
void stringWithExternalContentsExample(void) {
#define BufferSize 1000
CFMutableStringRef mutStr;
UniChar *myBuffer;
myBuffer = malloc(BufferSize * sizeof(UniChar));
mutStr = CFStringCreateMutableWithExternalCharactersNoCopy(NULL, myBuffer, 0,
BufferSize, kCFAllocatorNull);
CFStringAppend(mutStr, CFSTR("Appended string... "));
CFStringAppend(mutStr, CFSTR("More stuff... "));
CFStringAppendFormat(mutStr, NULL, CFSTR("%d %4.2f %@..."), 42, -3.14,
CFSTR("Hello"));
CFRelease(mutStr);
free(myBuffer);
}
这个例子阐述了你怎么样使用CFString函数去修改缓冲区的内容。你页可以直接修改缓冲区的内容,如果你这样做,你必须使用CFStringSetExternalCharactersNoCopy函数通知可变CFString对象。使用此函数你可以截取一个完整的不同的缓冲区,因为它可以让可变CFString对象直接指向指定的UniChar数组当做它的后备存储(然而,CFString对象必须已经用CFStringCreateMutableWithExternalCharactersNoCopy函数创建)。CFStringSetExternalCharactersNoCopy函数没有释放之前的缓冲区。
使用这些函数会带来一些资源耗费,因为一些CFString无法优化。举个例子,可变CFString对象在编辑中不能使用空格,他们不能使用8位字符优化存储。