版本记录
版本号 | 时间 |
---|---|
V1.0 | 2017.10.06 |
前言
Core Foundation
框架(CoreFoundation.framework)
是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能。接下来我们就详细的解析这个框架。感兴趣的可以看我上面写的几篇。
1. CoreFoundation框架详细解析(一) —— 基本概览
2. CoreFoundation框架详细解析(二) —— 设计概念
Introduction - 简介
对于管理内存,Core Foundation
使用分配器,引用计数机制以及由函数名称提示的对象所有权策略。 本主题介绍了创建,复制,保留和释放对象的相关技术。
内存管理是有效和高效地使用Core Foundation的基础。 本文档对于使用Core Foundation的所有开发人员来说都是必不可少的。
1. Organization of This Document - 文章组织
以下概念和任务讨论内置支持Core Foundation
提供的用于管理对象的内存分配和释放:
如果您需要自定义您的分配器,请阅读:
要了解更多有关字节排序和交换的信息,请参阅:
2. 其他参考
你也许感兴趣:
Allocators - 分配器
在操作系统服务中,Core Foundation
大要的是内存分配。它为此目的使用分配器。
分配器是为您分配和释放内存的不透明对象。您无需直接为Core Foundation对象分配,重新分配或释放内存,而且很少需要。将分配器传递给创建对象的函数,这些函数在其名称中嵌入了Create
,例如CFStringCreateWithPascalString
。创建函数使用分配器为其创建的对象分配内存。
分配器通过其生命周期与对象相关联。如果需要重新分配内存,则该对象使用分配器进行此目的,并且当对象需要释放时,分配器将用于对象的释放。分配器也用于创建最初创建的对象所需的任何对象。一些函数还允许您传递分配器用于特殊目的,例如释放临时缓冲区的内存。
Core Foundation允许您创建自己的自定义分配器。 Core Foundation还提供了一个系统分配器,并初始将此分配器设置为当前线程的默认分配器。 (每个线程有一个默认分配器。)您可以在代码中随时将自定义分配器设置为线程的默认分配器。然而,系统分配器是一个很好的通用分配器,对于几乎所有情况都应该是足够的。在特殊情况下可能需要自定义分配器,例如在Mac OS 9中的某些情况下,或者在性能出现问题时作为批量分配器。除了这些罕见的场合,您不应使用自定义分配器或将其设置为默认值,尤其是对于库。
有关分配器的更多信息,具体来说,有关创建自定义分配器的信息,请参阅Creating Custom Allocators。
Ownership Policy - 所有权政策
使用Core Foundation的应用程序不断访问和创建和处理对象。 为了确保不泄漏内存,Core Foundation定义了获取和创建对象的规则。
1. Fundamentals - 基本
在Core Foundation应用程序中尝试了解内存管理时,不要以内存管理本身来思考,而是以对象所有权为依据。 对象可能有一个或多个所有者;它使用保留计数记录拥有者的数量。 如果对象没有所有者(如果其保留计数下降到零),则它被处理(被释放)。 Core Foundation定义了对象所有权和处置的以下规则。
- 如果您创建对象(直接或通过制作另一个对象的副本 - 请参阅The Create Rule),您拥有它。
- 如果你从别的地方得到一个对象,你就不拥有它。 如果要防止它被处置,您必须将自己添加为所有者(使用CFRetain)。
- 如果您是对象的所有者,则必须在完成使用后放弃所有权(使用CFRelease)。
2. Naming Conventions - 命名约定
有很多方法可以使用Core Foundation获取对象的引用。根据Core Foundation所有权政策,您需要知道您是否拥有一个函数返回的对象,以便您知道在内存管理方面采取了哪些措施。 Core Foundation已经为其函数建立了一个命名约定,允许您确定是否拥有由函数返回的对象。简而言之,如果函数名称包含单词Create
或Copy
,那么您拥有该对象。如果函数名称包含单词Get
,则不拥有该对象。这些规则将在The Create Rule和The Get Rule中有更详细的解释。
重要提示:Cocoa定义了一组类似于内存管理的命名约定(参见Advanced Memory Management Programming Guide)。 Core Foundation的命名约定,特别是使用单词create
,仅适用于返回Core Foundation对象的C函数。 Objective-C方法的命名约定由Cocoa约定管理,不管该方法是返回Core Foundation还是Cocoa对象。
3. The Create Rule - 创建规则
Core Foundation函数的名称指示何时拥有返回的对象:
- 具有嵌入在名称中的
Create
的对象创建功能; - 具有嵌入在名称中的
Copy
的对象复制功能。
如果您拥有一个对象,那么在完成此操作后,您有责任放弃所有权(使用CFRelease)。
请考虑以下示例。 第一个示例显示了与CFTimeZone
相关联的两个创建函数,另一个与CFBundle
关联。
CFTimeZoneRef CFTimeZoneCreateWithTimeIntervalFromGMT (CFAllocatorRef allocator, CFTimeInterval ti);
CFDictionaryRef CFTimeZoneCopyAbbreviationDictionary (void);
CFBundleRef CFBundleCreate (CFAllocatorRef allocator, CFURLRef bundleURL);
第一个函数在其名称中包含单词Create
,并创建一个新的CFTimeZone
对象。 你拥有这个对象,你有责任放弃所有权。 第二个函数在其名称中包含单词Copy
,并创建时区对象属性的副本。 (注意,这不同于获取属性本身 - 请参阅The Get Rule。)同样,您拥有此对象,您有责任放弃所有权。 第三个函数CFBundleCreate
在其名称中包含单词Create
,但文档规定它可能返回现有的CFBundle
。 然而,再一次,你拥有这个对象,无论是否创建了一个新对象。 如果现有的对象被返回,则其保留计数将被递增,因此您有责任放弃所有权。
下一个示例可能看起来更复杂,但它仍然遵循相同的简单规则。
/* from CFBag.h */
CF_EXPORT CFBagRef CFBagCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFBagCallBacks *callBacks);
CF_EXPORT CFMutableBagRef CFBagCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFBagRef bag);
CFBag
函数CFBagCreateMutableCopy
在其名称中同时具有Create
和Copy
。 它是一个创建函数,因为函数名包含单词Create
。 还要注意,第一个参数是CFAllocatorRef
类型,这作为一个进一步的提示。 该函数中的Copy
是一个提示,该函数接受CFBagRef
参数并生成对象的副本。 它还指的是源集合的元素对象会发生什么:它们被复制到新创建的包中。 函数名称的Copy
和NoCopy
子字符串表示如何处理某些源对象拥有的对象,即它们是否被复制。
4. The Get Rule - 获取规则
如果您收到来自任何Core Foundation函数的对象,而不是创建或复制函数(例如Get函数),则您不拥有该对象,并且无法确定对象的使用寿命。 如果要确保在使用它时不会处理这样的对象,则必须声明所有权(使用CFRetain
函数)。 完成后,您将负责放弃所有权。
考虑CFAttributedStringGetString函数,它返回属性字符串的后备字符串。
CFStringRef CFAttributedStringGetString (CFAttributedStringRef aStr);
如果归因的字符串被释放,它将放弃支持字符串的所有权。 如果归因的字符串是支持字符串唯一的所有者,那么支持字符串现在没有所有者,它本身就被释放。 如果您在归因字符串被处理后需要访问支持字符串,则必须声明所有权(使用CFRetain
) - 或者复制它。 然后,您必须放弃所有权(使用CFRelease
),否则会创建内存泄漏。
5. Instance Variables and Passing Parameters - 实例变量和传递参数
基本规则的推论是,当您将对象传递给另一个对象(作为函数参数)时,应该期望接收者如果需要维护传递的对象,则可以拥有所传递的对象。
要理解这一点,把自己放在接收对象的实现者的位置。 当函数接收到一个对象作为参数时,接收方最初不拥有该对象。 因此,该对象可以在任何时候被释放 - 除非接收者拥有if(使用CFRetain
)的所有权。 当接收方完成对象时,或者是因为它被替换了一个新值,或者因为接收方本身被释放 - 接收方负责放弃所有权(使用CFRelease
)。
6. Ownership Examples - 所有权举例
为了防止运行时错误和内存泄漏,您应该确保在Core Foundation对象被接收,传递或返回的地方始终应用Core Foundation所有权策略。 要了解为什么可能需要成为您没有创建的对象的所有者,请考虑此示例。 假设你从另一个对象获取一个值。 如果值的containing
对象随后被释放,它放弃对contained
对象的所有权。 如果包含对象是该值的唯一所有者,则该值不具有所有者,并且也被释放。 您现在可以引用一个被释放的对象,如果您尝试使用它,您的应用程序将崩溃。
以下代码片段说明了三种常见的情况:一个Set访问器函数,一个Get访问器函数和一个保存在Core Foundation对象上的函数,直到满足某个条件。 首先Set函数:
static CFStringRef title = NULL;
void SetTitle(CFStringRef newTitle) {
CFStringRef temp = title;
title = CFStringCreateCopy(kCFAllocatorDefault , newTitle);
CFRelease(temp);
}
上述示例使用静态CFStringRef
变量来保存保留的CFString对象。您可以使用其他方式来存储它,但是您必须将其放在当然不在本地接收函数的某个地方。该函数将当前标题分配给本地变量,然后再复制新标题并释放旧标题。如果传入的CFString对象与当前持有的对象相同,它将在复制后释放。
请注意,在上述示例中,对象被复制而不是简单地保留。 (回想一下,从所有权角度看,这些是等效的 - 参见Fundamentals。)原因是title属性可能被认为是属性。这是不应该改变的东西,除非通过访问器方法。即使参数键入CFStringRef,也可能会传入对CFMutableString对象的引用,这将允许值外部更改的可能性。因此,您复制对象,以便在持有该对象时不会更改该对象。如果对象是或可以是可变的,您应该复制一个对象,并且需要您自己的唯一版本。如果对象被认为是关系,那么你应该保留它。
相应的Get函数要简单得多:
CFStringRef GetTitle() {
return title;
}
简单的返回一个对象,你将返回一个对它的弱引用。 换句话说,指针值被复制到接收器的变量,但引用计数不变。 返回集合中的元素时也会发生相同的情况。
以下函数retain
从集合中检索到的对象,直到它不再需要它,然后释放它。 假定该对象是不可变的。
static CFStringRef title = NULL;
void MyFunction(CFDictionary dict, Boolean aFlag) {
if (!title && !aFlag) {
title = (CFStringRef)CFDictionaryGetValue(dict, CFSTR(“title”));
title = CFRetain(title);
}
/* Do something with title here. */
if (aFlag) {
CFRelease(title);
}
}
以下示例显示将number
对象传递给数组。 数组的回调指定添加到集合中的对象被retained
(集合拥有它们),因此该number可以在添加到数组后被释放。
float myFloat = 10.523987;
CFNumberRef myNumber = CFNumberCreate(kCFAllocatorDefault,
kCFNumberFloatType, &myFloat);
CFMutableArrayRef myArray = CFArrayCreateMutable(kCFAllocatorDefault, 2, &kCFTypeArrayCallBacks);
CFArrayAppendValue(myArray, myNumber);
CFRelease(myNumber);
// code continues...
请注意,如果(a)您释放阵列,并且(b)在释放数组后继续使用number变量,则存在潜在的陷阱:
CFRelease(myArray);
CFNumberRef otherNumber = // ... ;
CFComparisonResult comparison = CFNumberCompare(myNumber, otherNumber, NULL);
除非保留number或数组,否则传递给维护其所有权的其他对象,代码将在比较函数中失败。 如果没有其他对象拥有数组或number,当数组被释放时,它也被释放,因此释放其内容。 在这种情况下,这也将导致number的释放,因此比较函数将在释放的对象上运行,从而崩溃。
后记
未完,待续~~~