本文翻译整理自:Core Foundation Design Concepts(更新日期:2013-12-16
https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFDesignConcepts/CFDesignConcepts.html#//apple_ref/doc/uid/10000122i
文章目录
- 一、核心基础设计概念简介
- 本文件的组织
- 二、不透明类型
- 不透明类型的优势
- 三、对象引用
- 四、多态函数
- 五、物体的种类
- 六、命名约定
- 七、其他类型
- 八、比较对象
- 九、检查对象
- 十、免费桥接类型
- 强制转换和对象生命周期语义
- 免费桥接类型
一、核心基础设计概念简介
Core Foundation是一个库,具有一组编程接口,这些接口在概念上源自基于Objective-C的Foundation框架,但用C语言实现。
为此,Core Foundation在C中实现了一个有限的对象模型。
Core Foundation定义了封装数据和函数的不透明类型,以下称为“对象”
Core Foundation对象的编程接口旨在易于使用和重用。
在一般层面上,Core Foundation:
- 支持在各种框架和库之间共享代码和数据
- 使某种程度的操作系统独立性成为可能
- 支持Unicode字符串internationalization
- 提供通用API和其他有用的功能,包括插件架构、XML属性列表和首选项
Core Foundation使OS X上的不同框架和库能够共享代码和数据。
应用程序、库和框架可以定义在其外部接口中包含Core Foundation类型的C例程;因此,它们可以通过这些接口相互通信数据——作为Core Foundation对象。
Core Foundation还在某些服务和Cocoa的Foundation框架之间提供“免费桥接”。
免费桥接使您能够在函数参数中将Cocoa对象替换为Core Foundation对象,反之亦然。
一些Core Foundation类型和函数是在不同操作系统上具有特定实现的事物的抽象。
因此,使用这些API的代码更容易移植到不同的平台。
日期和数字类型抽象了时间实用程序,并提供了在绝对时间度量和公历时间度量之间转换的工具。
它还抽象了数值,并提供了在这些值的不同内部表示之间转换的工具。
Core Foundation给应用程序开发带来的主要好处之一是internationalization支持。
通过其String对象,Core Foundation促进了所有OS X和Cocoa编程接口和实现的简单、健壮和一致的internationalization。
这种支持的基本部分是一种类型CFString,其实例表示16位Unicode字符的数组。
CFString对象足够灵活,可以容纳数兆字节的字符,但也足够简单和低级,可以用于所有通信字符数据的编程接口。
它的性能与标准C字符串没有太大区别。
您应该阅读本文档以了解核心基础的基本设计原则,以及核心基础对象如何与可可(触摸)对象交互。
本文件的组织
这些概念和任务讨论了Core Foundation中使用的对象模型:
- 不透明类型
- 对象引用
- 多态函数
- 物体的种类
- 比较对象
- 检查对象
此外,在使用Core Foundation之前,您应该熟悉其他非对象类型和API约定:
- 命名约定
- 其他类型
- 免费桥接类型
二、不透明类型
Core Foundation支持封装和多态函数的对象模型基于不透明类型。
基于不透明类型的对象的各个字段对客户端是隐藏的,但是该类型的函数提供对这些字段的大多数值的访问。
图1描述了它“隐藏”的数据和呈现给客户端的界面中的不透明类型。
注意:“类”不用于指代不透明类型,因为尽管类和不透明类型在概念上相似,但许多人可能会觉得这个术语令人困惑。
然而,核心基金会留档经常将这些类型的特定数据承载实例称为“对象”
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函数的参数和返回值采用这些对象引用的类型,而不是私有结构的typedef
。
例如:
CFStringRef CFStringCreateByCombiningStrings(CFAllocatorRef alloc, CFArrayRef array, CFStringRef separatorString);
有关不可变、可变和其他不透明类型对象变体的更多信息,请参阅对象的变体。
每个Core Foundation不透明类型都为其对象定义了一个唯一的类型ID,就像上面的CFArrayRef
中CFArray对象一样。
类型ID是CFTypeID
类型的整数,用于标识Core Foundation对象“所属”的不透明类型。
您可以在各种上下文中使用类型ID,例如在异构集合上操作时。
Core Foundation提供了用于获取和评估类型ID的编程接口。
重要提示:由于类型ID的值可以在不同版本之间更改,因此您的代码不应依赖于存储或硬编码的类型ID,也不应硬编码类型ID的任何观察到的属性(例如,它是一个小整数)。
此外,Core Foundation定义了一个通用的对象引用类型CFTypeRef
,类似于某些面向对象编程语言中的根类。
此通用引用用作多态函数的参数和返回值的占位符类型,可以引用任何Core Foundation对象。
有关此主题的更多信息,请参阅多态函数。
有关使用对象引用时与内存管理相关的问题,请参阅 Core Foundation的内存管理编程指南 。
四、多态函数
Core Foundation提供了几个多态函数。
这些函数可以将任何Core Foundation对象作为参数,并且(在一个实例中,CFRetain
)可以返回任何Core Foundation对象。
这些参数和返回值被赋予CFTypeRef
的类型,一种通用的对象引用类型。
CFType类似于面向对象语言中的根类,因为它的函数可以被所有其他对象重用。
您将多态函数用于所有Core Foundation对象通用的操作:
- 引用计数。
CFType提供了几个多态函数来操作和获取对象的引用计数。
有关这些函数的更多信息,请参阅 Core Foundation的内存管理编程指南 。 - 比较对象。
这个函数CFEqual
任意两个Core Foundation对象(见比较对象)。
相等的基础取决于所比较对象的类型。
例如,如果两个对象都是CFString对象,则测试涉及character-by-character比较。 - 散列对象。
这个CFHash
函数返回一个唯一的哈希码,标识一个Core Foundation对象(参见比较对象)。
您可以使用哈希码作为哈希表结构中的表地址。
如果两个对象相等(由CFEqual
函数确定),它们必须具有相同的哈希值。 - 检查对象。
CFType为您提供了检查对象的方法,从而了解它们的内容和它们“所属”的类型。
CFCopyDescription
函数返回描述对象的字符串(更准确地说,是对CFString对象的引用)。
CFCopyTypeIDDescription
函数采用CFTypeID
而不是CFTypeRef
参数,返回描述由类型ID标识的不透明类型的字符串引用。
这些函数主要用于帮助调试;有关这些函数的更多信息,请参阅检查对象。
您还可以通过使用CFGetTypeID
函数获取泛型类型对象的类型ID,然后将该值与已知类型ID进行比较来确定泛型类型对象所属的不透明类型。
有关此任务的详细信息,请参阅检查对象。
五、物体的种类
不透明类型根据其对象的可编辑性和可扩展性特征,多达三种基本变体或“风味”:
- 不可变和固定大小
- 可变和固定大小
- 可变和可变大小
可变对象是可编辑的,这意味着它们的内容可以更改。
不可变对象是不可编辑的;一旦创建,它们就不能更改。
任何更改不可变对象的尝试通常会导致某种错误。
固定大小的对象有它可以增长到的最大限制;对于CFString,这将是字符数,对于集合,限制将是元素数。
一些不透明类型,如CFString和CFArray,可以创建所有三种类型的对象。
大多数不透明类型可以创建不可变的、固定大小的对象,并且通常至少有一个不合格的创建函数来完成这项工作(如CFArrayCreate
)。
可变固定大小与可变大小的决定因素是类型CreateMutable
函数中容量或最大长度参数的值;任何正值都会产生固定大小的对象,但0指定可变大小的对象。
对可变对象的引用在类型名称中包含“Mutable”,例如CFMutableStringRef
。
六、命名约定
Core Foundation中的一个主要programming-interface约定是使用与符号最密切相关的不透明类型的名称作为符号的前缀。
对于函数,此前缀不仅标识函数“所属”的类型,而且通常标识作为函数操作目标的对象的类型。
(此约定的一个例外是常量,它将“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还有一些programming-interface约定。
- 在返回值的函数名称中,Get、Copy和Create之间有一个重要的区别。
如果使用Get函数,则无法确定返回对象的生命周期。
为了确保此类对象的持久性,您可以保留它(使用CFRetain函数),或者在某些情况下复制它。
如果使用Copy或Create函数,则负责释放对象(使用CFRelease函数)。
有关详细信息,请参阅 Core Foundation的内存管理编程指南 。 - 一些Core Foundation对象有自己的命名约定,以在常见操作之间施加一致性。
例如,集合在函数名称中嵌入以下动词以表示对集合元素的特定操作:- “添加”表示“如果不存在则添加,如果存在则不执行任何操作”(如果是唯一集合)。
- “替换”意味着“如果存在则替换,如果不存在则不做任何事情。”
- “设置”意味着“如果不存在则添加,如果存在则替换。”
- “删除”的意思是“如果存在则删除,如果不存在则不做任何事情。”
- CFIndex
CFIndex
类型用于索引、计数、长度和大小参数以及返回值。
此类型表示的整数值(当前为32位)可以随着处理器地址大小的增长而增长。
在指针大小不同的架构上,例如64位,CFIndex
可以声明为64位,与int
的大小无关。
通过对与同一类型的Core Foundation参数交互的变量使用CFIndex
,您可以确保代码具有更高程度的源代码兼容性。 - 一些Core Foundation头文件似乎定义了不透明的类型,但实际上包含与特定类型无关的便利函数。
一个典型的例子是CFPropertyList.h
。
CFProperty tyList是任何属性列表类型的占位符类型:CFString、CFData、CFBoolean、CFNumber、CFDate、CFArray和CFDictionr。 - 除非另有说明,所有用于返回值的引用参数都可以接受
NULL
。
这表明调用者对该返回值不感兴趣。
七、其他类型
Core Foundation定义了许多函数中常用的数据类型。
其中一些类型的目的是抽象出可能随着处理器地址空间变化而改变的原始值。
例如,CFIndex类型用于索引、计数、长度和大小参数。
CFOptionFlags类型用于位域参数,CFHashCode类型保存从CFHash
函数返回的散列结果和某些散列回调。
其他基本类型用于获取和返回比较和范围值的函数中。
CFRange是一种结构,它指定项目线性序列的任何部分,从字符串中的字符到集合中的元素。
对于比较函数,CFCompisonResult类型定义enum
常量来表示适当的返回值(等于、小于、大于)。
一些Core Foundation函数对比较器函数进行回调;如果需要自定义比较器,函数必须符合CFComparatorFunction类型指定的签名。
重要提示:某些Core Foundation类型(特别是CFIndex和CFTypeID)的整数值会随着处理器地址大小的增长而增长。
通过对与同一类型的Core Foundation参数交互的变量使用基本类型,您将确保代码具有更高程度的源代码兼容性。
Core Foundation提供的其他不透明类型将在单独的主题中讨论。
八、比较对象
您将两个Core Foundation对象与CFEqual
函数进行比较。
如果两个对象本质上相等,则该函数返回布尔真值。
“基本”相等性取决于比较的对象类型。
例如,当您比较两个CFString对象时,Core Foundation认为它们在逐个字符匹配时本质上相等,而不管它们的编码或可变属性如何。
当两个CFArray对象具有相同的元素计数时,它们被认为是相等的并且一个数组中的每个元素对象与另一个数组中的对应对象本质上相等。
显然,比较的对象必须具有相同的类型(或相同类型的可变或不可变变体)才能被认为是相等的。
下面的代码片段展示了如何使用CFEqual
函数将常量与传入参数进行比较:
void stringTest(CFStringRef myString) {
Boolean equal = CFEqual(myString, CFSTR(“Kalamazoo”));
if (!equal) {
printf(“They’re not equal!");
}
else {
printf(“They’re equal!”):
}
}
九、检查对象
Core Foundation对象的一个主要特征是它们基于不透明(或私有)类型;因此很难直接检查对象的内部数据。
然而,Base Services提供了两个函数,您可以使用它们检查Core Foundation对象。
这些函数返回对象和对象类型的描述。
要查找Core Foundation对象的内容,请在该对象上调用CFCopyDescription
函数,然后打印引用的字符串对象中“包含”的字符序列:
例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);
}
这个例子只展示了打印描述的一种方法。
您可以使用CFStringGetBytes
以外的CFString函数来获取实际字符串。
要确定未知对象的类型,请使用CFGetTypeID
函数获取其类型ID,并将该值与已知类型ID进行比较,直到找到匹配项。
您可以使用CFGetTypeID
函数获取对象的类型ID。
每个不透明类型还定义了CF
TypeGetTypeID
形式的函数(例如,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.h
中声明,您可以调用支持的调试器来打印Core Foundation对象的描述:CFShow
和CFShowStr
。
重要提示: CFCopyDescription 的CFCopyDescription
和CFCopyTypeIDDescription
函数仅用于调试。
因为描述中的信息及其格式可能会发生变化,所以不要在代码中创建对它们的依赖关系。
十、免费桥接类型
核心基础框架和基础框架中有许多数据类型可以互换使用。
可以互换使用的数据类型也称为免费桥接数据类型。
这意味着您可以使用相同的数据结构作为核心基础函数调用的参数或作为Objective-C消息调用的接收者。
例如,NSLocale
(参见 NSLocale类参考 )可以与其核心基础对应的CFLocale(参见 CFLocale参考 )互换。
并非所有数据类型都是免费桥接的,尽管它们的名称可能表明它们是免费桥接的。
例如,NSRunLoop
不是免费桥接到CFRunLoop的,NSBundle
不是免费桥接到CFBundle的,NSDateFormatter
不是免费桥接到CFDateForware的。
表1提供了支持免费桥接的数据类型列表。
注意: 如果您在正在使用的Core Foundation集合上安装自定义回调,包括NULL
回调,则从Objective-C访问时,其内存管理行为未定义。
强制转换和对象生命周期语义
通过免费桥接,在您看到例如NSLocale *
参数的方法中,您可以传递CFLocaleRef
,在您看到CFLocaleRef
参数的函数中,您可以传递NSLocale
实例。
您还必须为编译器提供其他信息:首先,您必须将一种类型转换为另一种类型;此外,您可能必须指示对象生命周期语义学。
编译器理解返回Core Foundation类型并遵循历史Cocoa命名约定的Objective-C方法(参见 高级内存管理编程指南 )。
例如,编译器知道,在iOS中,UIColor
的CGColor方法返回的CGColor
不属于所有。
您仍然必须使用适当的类型转换,如下例所示:
NSMutableArray *colors = [NSMutableArray arrayWithObject:(id)[[UIColor darkGrayColor] CGColor]];
[colors addObject:(id)[[UIColor lightGrayColor] CGColor]];
编译器不会自动管理Core Foundation对象的生命周期。
您可以使用强制转换(在objc/runtime.h
中定义)或Core Foundation样式的宏(在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的版本,其中它们之间的免费桥接可用。
Table 1 Data types that can be used interchangeably between Core Foundation and Foundation
核心基础类型 | 基础班 | 可用性 |
---|---|---|
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 |
2024-06-16(日)