Core Foundation设计概念介绍Core Foundation是一个库,它有一组编程接口,从基于Objective-C的Foundation框架概念上派生出来,但用C语言实现。为此,Core Foundation在c中实现了一个有限的对象模型。Core Foundation定义了封装数据和函数的不透明类型,以下简称“对象”。
Core Foundation对象的编程接口被设计为易于使用和重用。一般来说,Core Foundation:
- 允许在各种框架和库之间共享代码和数据
- 使某种程度的操作系统独立性成为可能
- 支持Unicode字符串的国际化
- 提供公共API和其他有用的功能,包括插件体系结构、XML属性列表和首选项
Core Foundation还提供了某些服务与Foundation框架之间的“免费桥接”。免费桥接允许你在函数参数中用Cocoa对象替代Core Foundation对象,反之亦然。
一些Core Foundation类型和函数是在不同操作系统上具有特定实现的事物的抽象。因此,使用这些api的代码更容易移植到不同的平台。
日期和数字类型抽象的时间工具,并提供设施之间的转换的时间的绝对和公历措施。它还抽象数值,并提供在这些数值的不同内部表示之间进行转换的工具。
Core Foundation为应用程序开发带来的主要好处之一是国际化支持。通过它的字符串对象,Core Foundation在所有OS X和Cocoa编程接口和实现中促进了简单、健壮和一致的国际化。这种支持的关键部分是类型CFString,它的实例表示一个16位Unicode字符数组。CFString对象足够灵活,可以容纳兆字节大小的字符,但也足够简单和低级,可以在所有通信字符数据的编程接口中使用。它的性能与与标准C字符串相关联的性能相差不大。
你应该阅读这个文档来了解Core Foundation的基本设计原则,以及Core Foundation对象如何与Cocoa(触摸)对象交互。
本文的组织
这些概念和任务讨论了Core Foundation中使用的对象模型:
- 不透明的类型
- 对象引用
- 多态函数
- 对象的类型
- 比较对象
- 检查对象
- 命名约定
- 其他类型
- 免费连接类型
基于不透明类型的对象的各个字段对客户端是隐藏的,但是类型的函数提供对这些字段的大多数值的访问。图1在它“隐藏”的数据和它呈现给客户端的接口中描述了一个不透明类型。
注意:“Class”并不是用来指代不透明类型的,因为尽管Class和不透明类型在概念上有相似之处,但许多人可能会对这个术语感到困惑。然而,Core Foundation文档经常将这些类型的特定的、带有数据的实例称为“对象”。
Core Foundation有许多不透明类型,这些类型的名称反映了它们的预期用途。例如,CFString是一种不透明的类型,它“表示”并对Unicode字符数组进行操作。(“CF”当然是Core Foundation的前缀。)CFArray是基于索引的集合功能的不透明类型。支持不透明类型的函数、常量和其他辅助数据类型通常在具有类型名称的头文件中定义,例如CFArray.h包含CFArray类型的符号定义。
图1不透明类型
文章图片
不透明类型的优点
对某些人来说,不透明类型可能会阻止直接访问结构的内容,从而造成不必要的限制。与不透明类型相关的开销可能会影响程序性能。但是不透明类型的好处超过了这些表面上的限制。
不透明类型为底层功能的实现提供了更好的抽象和更大的灵活性。通过隐藏结构的字段等细节,Core Foundation减少了这些细节更改时客户端代码中可能发生错误的机会。此外,不透明类型允许进行优化,如果公开这些优化可能会造成混乱。例如,CFString“正式”表示UniChar类型的16位字符数组。但是,CFString可以选择将ASCII范围内的字符存储为8位值。复制一个不可变的对象可能(通常也确实)会导致对该对象的共享引用,而不是在内存中创建两个独立的对象(参见Core Foundation的内存管理编程指南)。
继续以CFString为例,使用不透明类型来存储字符似乎很重要。然而,事实证明,这种存储的CPU成本并不比使用简单的C字符数组高多少,而且内存成本通常更低。另外,不透明并不一定意味着不透明类型永远不能提供直接访问内容的机制。例如,CFString为此提供了CFStringGetCStringPtr函数。
最后,你可以在一定程度上自定义一些不透明类型。例如,集合类型允许你为在集合的每个成员上调用函数定义回调。
对象引用通过引用Core Foundation对象(不透明类型)。在每个不透明类型的头文件中,你会注意到一行或两行类似于以下内容:
typedef const struct __CFArray * CFArrayRef;
typedef struct __CFArray * CFMutableArrayRef;
诸如此类的声明是指向定义不透明类型的(私有)结构的不可变和可变版本的指针引用。许多Core Foundation函数的参数和返回值都采用这些对象引用的类型,并且从不对私有结构进行类型定义。例如:
CFStringRef CFStringCreateByCombiningStrings(CFAllocatorRef alloc, CFArrayRef array, CFStringRef separatorString);
有关不可变、可变和其他不透明类型对象的变体的更多信息,请参见对象类型。
每个Core Foundation不透明类型为它的对象定义一个唯一的类型ID,就像上面CFArray对象的CFArrayRef中那样。类型ID是类型CFTypeID的整数,它标识了Core Foundation对象所属的不透明类型。“你可以在各种上下文中使用类型id,例如在异构集合上进行操作时。Core Foundation提供了用于获取和评估类型id的编程接口。
重要:因为类型ID的值可以在不同版本之间更改,所以你的代码不应该依赖于存储的或硬编码的类型ID,也不应该硬编码类型ID的任何观察到的属性(例如,它是一个小整数)。
此外,Core Foundation定义了一个通用的对象引用类型CFTypeRef,类似于一些面向对象编程语言中的根类。这个通用引用作为多态函数的参数和返回值的占位符类型,它可以引用任何Core Foundation对象。有关此主题的更多信息,请参见多态函数。有关使用对象引用时内存管理问题的核心基础,请参阅内存管理编程指南。
多态函数Core Foundation提供了多种多态函数。这些函数可以接受任何Core Foundation对象作为参数,并且(在一个实例中,CFRetain)可以返回任何Core Foundation对象。这些参数和返回值的类型是CFTypeRef,这是一种通用的对象引用类型。CFType类似于面向对象语言中的根类,因为它的函数可以被所有其他对象重用。
你使用多态函数的操作通用的所有Core Foundation对象:
- 引用计数。
- CFType提供了几个多态函数来操作和获取对象的引用计数。有关这些函数的更多信息,请参见Core Foundation的内存管理编程指南。
- 比较对象。
- CFEqual函数比较任意两个Core Foundation对象(参见比较对象)。相等的基础取决于比较对象的类型。例如,如果两个对象都是CFString对象,则测试将涉及逐字符比较。
- 哈希对象。
- CFHash函数返回一个惟一的散列代码,该散列代码标识一个Core Foundation对象(参见比较对象)。可以将哈希代码用作哈希表结构中的表地址。如果两个对象相等(由CFEqual函数决定),它们必须具有相同的散列值。
- 检查对象。
- CFType提供了检查对象的方法,从而了解对象的内容和它们所属的类型。CFCopyDescription函数返回一个字符串(更准确地说,是对CFString对象的引用),该字符串描述一个对象。CFCopyTypeIDDescription函数采用CFTypeID而不是CFTypeRef参数,它返回一个字符串引用,该引用描述由类型ID标识的不透明类型。有关这些函数的更多信息,请参见检查对象。
- 你还可以通过使用CFGetTypeID函数获取其类型ID,然后将该值与已知类型ID进行比较,从而确定泛型对象所属的不透明类型。有关此任务的更多信息,请参见检查对象。
- 不可变和固定大小
- 可变和固定大小
- 可变大小
一些不透明的类型,如CFString和CFArray,可以创建所有这三种类型的对象。大多数不透明类型可以创建不可变的、固定大小的对象,并且通常至少有一个非限定的创建函数来完成这项工作(例如CFArrayCreate)。可变固定大小与可变大小的行列式是TypeCreateMutable函数中的容量或最大长度参数的值; 任何正值都会产生一个固定大小的对象,而0则指定一个可变大小的对象。
对可变对象的引用包括类型名中的“CFMutableStringRef”。
命名约定Core Foundation中的一个主要编程接口约定是使用与符号密切相关的不透明类型的名称作为符号的前缀。对于函数,这个前缀不仅标识函数“所属”的类型,而且通常标识函数操作的目标对象的类型。(这个约定的一个例外是常量,它将“k”放在类型前缀之前。)以下是一些来自头文件的例子:
/* from CFDictionary.h */
CF_EXPORT CFIndex CFDictionaryGetCountOfKey(CFDictionaryRef dict, const void *key);
/* from CFString.h */
typedef UInt32 CFStringEncoding;
/* from CFCharacterSet.h */
typedef enum {
kCFCharacterSetControl = 1,
kCFCharacterSetWhitespace,
kCFCharacterSetWhitespaceAndNewline,
kCFCharacterSetDecimalDigit,
kCFCharacterSetLetter,
kCFCharacterSetLowercaseLetter,
kCFCharacterSetUppercaseLetter,
kCFCharacterSetNonBase,
kCFCharacterSetDecomposable,
kCFCharacterSetAlphaNumeric,
kCFCharacterSetPunctuation,
kCFCharacterSetIllegal
} CFCharacterSetPredefinedSet;
Core Foundation除了与不透明类型和内存管理相关的约定外,还有一些编程接口约定。
- 在返回值的函数的名称中,Get、Copy和Create之间有一个重要的区别。如果使用Get函数,则无法确定返回对象的生命周期。为了确保这样一个对象的持久性,你可以保留它(使用CFRetain函数),或者在某些情况下复制它。如果你使用一个Copy或Create函数,那么你需要负责释放该对象(使用CFRelease函数)。有关详细信息,请参阅Core Foundation的内存管理编程指南。
- 一些Core Foundation对象有自己的命名约定,以便在公共操作之间实现一致性。例如,集合在函数名中嵌入以下动词,表示对集合元素的特定操作:
- “Add”的意思是“缺少时添加,存在时不做”(如果是一个独特的集合)。
- “Replace”的意思是“存在时代替,缺少时不做任何事”。
- “Set”的意思是“如无添加,如有替换”。
- “Remove”的意思是“如果存在,删除; 如果没有,什么也不做。”
- CFIndex类型用于索引、计数、长度和大小参数以及返回值。该类型表示的整数值(当前为32位)可以随着处理器地址大小的增长而增长。在指针大小不同的架构上,比如64位,CFIndex可能被声明为64位,与int的大小无关。
- 一些Core Foundation头文件似乎定义了不透明的类型,但实际上包含了与特定类型无关的便利函数。一个恰当的例子是CFPropertyList.h。CFPropertyList是任何属性列表类型的占位符类型:CFString、CFData、CFBoolean、CFNumber、CFDate、CFArray和CFDictionary。
- 除非另外指定,否则用于返回值的所有by-reference参数都可以接受NULL。这表明调用者对返回值不感兴趣。
其他基类型用于接受和返回比较和范围值的函数。CFRange是一种结构,它指定项目的线性序列的任何部分,从字符串中的字符到集合中的元素。对于比较函数,CFComparisonResult类型定义enum常量来表示适当的返回值(等于、小于、大于)。一些Core Foundation函数对比较函数进行回调;如果需要自定义比较器,则该函数必须符合CFComparatorFunction类型指定的签名。
重要提示:某些Core Foundation类型(尤其是CFIndex和CFTypeID)的整数值可能随着处理器地址大小的增长而增长。通过为与同一类型的核心基础参数交互的变量使用基类型,你将确保代码具有更高程度的源代码兼容性。
Core Foundation提供的其他不透明类型将在单独的主题中讨论。
比较对象将两个Core Foundation对象用CFEqual函数进行比较。如果两个对象本质上相等,则函数返回一个布尔真值。“本质的”相等性取决于比较对象的类型。例如,当你比较两个CFString对象时,Core Foundation认为当它们逐个字符匹配时,本质上是相等的,而不考虑它们的编码或可变性属性。当两个CFArray对象具有相同的元素计数且一个数组中的每个元素对象与另一个数组中的对应元素对象本质上相等时,则认为它们是相等的。显然,比较对象必须具有相同的类型(或相同类型的可变或不可变变体)才能被认为是相等的。
下面的代码片段展示了如何使用CFEqual函数来比较常量和传入的参数:
清单1比较Core Foundation对象
void stringTest(CFStringRef myString) {
Boolean equal = CFEqual(myString, CFSTR(“Kalamazoo”));
if (!equal) {
printf(“They’re not equal!");
}
else {
printf(“They’re equal!”):
}
}
检查对象类型检查使用类型ID。Core Foundation对象的一个主要特征是它们基于不透明(或私有)类型; 因此,很难直接检查对象的内部数据。但是,基本服务提供了两个可以检查Core Foundation对象的功能。这些函数返回对象和对象类型的描述。
要查找Core Foundation对象的内容,请调用该对象上的CFCopyDescription函数,然后打印引用到string对象中的包含的字符序列:
清单1使用CFCopyDescription
void describe255(CFTypeRef tested) {
char buffer[256];
CFIndex got;
CFStringRef description = CFCopyDescription(tested);
CFStringGetBytes(description,
CFRangeMake(0, CFStringGetLength(description)),
CFStringGetSystemEncoding(), '?', TRUE, buffer, 255, &
got);
buffer[got] = (char)0;
fprintf(stdout, "%s", buffer);
CFRelease(description);
}
本例仅展示了打印描述的一种方法。你可以使用CFString函数而不是CFStringGetBytes来获得实际的字符串。
要确定“未知”对象的类型,请使用CFGetTypeID函数获取其类型ID,并将该值与已知类型ID进行比较,直到找到匹配为止。使用CFGetTypeID函数获得对象的类型ID。每个不透明类型还定义了一个函数的形式CFTypeGetTypeID(例如,CFArrayGetTypeID); 此函数返回该类型的类型ID。因此,你可以测试一个CFType对象是否是一个特定不透明类型的成员,如下所示:
CFTypeID type = CFGetTypeID(anObject);
if (CFArrayGetTypeID() == type)
printf(“anObject is an array.”);
else
printf(“anObject is NOT an array.”);
要在调试器中显示关于Core Foundation对象类型的信息,使用CFGetTypeID函数来获取其类型ID,然后将该值传递给CFCopyTypeIDDescription函数:
/* aCFObject is any Core Foundation object */
CFStringRef descrip = CFCopyTypeIDDescription(CFGetTypeID(aCFObject));
注意:字符串服务包含两个函数,都在CFString中声明。你可以调用受支持的调试器来打印Core Foundation对象的描述:CFShow和CFShowStr。
重要提示:CFCopyDescription和CFCopyTypeIDDescription函数仅用于调试。因为描述中的信息及其格式可能会更改,所以不要在代码中创建对它们的依赖关系。
免费连接类型在Core Foundation框架和Foundation框架中有许多数据类型可以互换使用。可以互换使用的数据类型也称为免费桥接数据类型。这意味着你可以使用相同的数据结构作为Core Foundation函数调用的参数,或者作为Objective-C消息调用的接收者。例如,NSLocale(请参阅NSLocale类参考)与它的Core Foundation对等物CFLocale(请参阅CFLocale参考)是可互换的。
并非所有数据类型都是免费桥接的,尽管它们的名称可能表明它们是免费的。例如,NSRunLoop不是免费桥接到CFRunLoop, NSBundle不是免费桥接到CFBundle, NSDateFormatter不是免费桥接到CFDateFormatter。表1提供了支持免费桥接的数据类型列表。
注意:如果你在你使用的Core Foundation集合上安装了一个自定义回调,包括一个空回调,它的内存管理行为在从Objective-C访问时是未定义的。
转换和对象生存期语义
通过免费桥接,在你看到例如NSLocale *参数的方法中,你可以传递CFLocaleRef,在你看到CFLocaleRef参数的函数中,你可以传递NSLocale实例。还必须为编译器提供其他信息:首先,必须将一种类型转换为另一种类型; 此外,你可能必须指出对象生存期语义。
编译器理解Objective-C方法,这些方法返回Core Foundation类型并遵循历史的Cocoa命名约定(参见高级内存管理编程指南)。例如,编译器知道在iOS中,UIColor的CGColor方法返回的CGColor是不属于它的。你仍然必须使用适当的类型转换,如下例所示:
NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];
[colors addObject:(id)[[UIColor lightGrayColor] CGColor]];
编译器不会自动管理Core Foundation对象的生存期。你告诉编译器对象的所有权语义,要么使用一个cast(在objc/runtime.h中定义),要么使用一个Core Foundation-style宏(在NSObject.h中定义):
- 在没有所有权转移的情况下,__bridge在Objective-C和Core Foundation之间传递一个指针。
- __bridge_retained 或 CFBridgingRetain将一个Objective-C指针强制转换为一个Core Foundation指针,并将所有权转移给你。
- 你负责调用CFRelease或相关函数来放弃对象的所有权。
- __bridge_transfer 或 CFBridgingRelease将一个非Objective-C指针移动到Objective-C,并将所有权转移到ARC。
- ARC负责放弃该对象的所有权。
NSLocale *gbNSLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"];
CFLocaleRef gbCFLocale = (__bridge CFLocaleRef)gbNSLocale;
CFStringRef cfIdentifier = CFLocaleGetIdentifier(gbCFLocale);
NSLog(@"cfIdentifier: %@", (__bridge NSString *)cfIdentifier);
// Logs: "cfIdentifier: en_GB"
CFLocaleRef myCFLocale = CFLocaleCopyCurrent();
NSLocale *myNSLocale = (NSLocale *)CFBridgingRelease(myCFLocale);
NSString *nsIdentifier = [myNSLocale localeIdentifier];
CFShow((CFStringRef)[@"nsIdentifier: " stringByAppendingString:nsIdentifier]);
// Logs identifier for current locale
下一个例子展示了Core Foundation内存管理函数的使用,这些函数由Core Foundation内存管理规则指定:
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGFloat locations[2] = {0.0, 1.0};
NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];
[colors addObject:(id)[[UIColor lightGrayColor] CGColor]];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
CGColorSpaceRelease(colorSpace);
// Release owned Core Foundation object.
CGPoint startPoint = CGPointMake(0.0, 0.0);
CGPoint endPoint = CGPointMake(CGRectGetMaxX(self.bounds), CGRectGetMaxY(self.bounds));
CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint,
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
CGGradientRelease(gradient);
// Release owned Core Foundation object.
}
免费桥接类型
表1提供了Core Foundation和Foundation之间可互换的数据类型列表。对于每一对,表中还列出了OS X的版本,其中可以使用它们之间的免费桥接。
【Core Foundation编程概念全解】表1可以在Core Foundation和Foundation之间交换使用的数据类型
Core Foundation type | Foundation class | 可用性 |
---|---|---|
CFArrayRef | NSArray | OS X 10.0 |
CFAttributedStringRef | NSAttributedString | OS X 10.4 |
CFBooleanRef | NSNumber | OS X 10.0 |
CFCalendarRef | NSCalendar | OS X 10.4 |
CFCharacterSetRef | NSCharacterSet | OS X 10.0 |
CFDataRef | NSData | OS X 10.0 |
CFDateRef | NSDate | OS X 10.0 |
CFDictionaryRef | NSDictionary | OS X 10.0 |
CFErrorRef | NSError | OS X 10.5 |
CFLocaleRef | NSLocale | OS X 10.4 |
CFMutableArrayRef | NSMutableArray | OS X 10.0 |
CFMutableAttributedStringRef | NSMutableAttributedString | OS X 10.4 |
CFMutableCharacterSetRef | NSMutableCharacterSet | OS X 10.0 |
CFMutableDataRef | NSMutableData | OS X 10.0 |
CFMutableDictionaryRef | NSMutableDictionary | OS X 10.0 |
CFMutableSetRef | NSMutableSet | OS X 10.0 |
CFMutableStringRef | NSMutableString | OS X 10.0 |
CFNullRef | NSNull | OS X 10.2 |
CFNumberRef | NSNumber | OS X 10.0 |
CFReadStreamRef | NSInputStream | OS X 10.0 |
CFRunLoopTimerRef | NSTimer | OS X 10.0 |
CFSetRef | NSSet | OS X 10.0 |
CFStringRef | NSString | OS X 10.0 |
CFTimeZoneRef | NSTimeZone | OS X 10.0 |
CFURLRef | NSURL | OS X 10.0 |
CFWriteStreamRef | NSOutputStream | OS X 10.0 |
推荐阅读
- 入门编程介绍 – Objective-C编程快速入门教程
- Objective-C线程技术(线程同步和线程安全)
- Objective-C运行时Runtime完全解读
- iOS内存管理(引用计数、Runloop、AutoreleasePool和引用循环)
- 快速了解iOS内存管理
- 内存管理之(__bridge、__bridge_transfer、__bridge_retained)
- GUI编程基本原理之(event loop和run loop(运行循环))
- Objective-C内存管理完全解读
- Objective-C Block用法完全解读