iOS内存管理--Core Foundation

Core Foundation使用引用计数,对象记录使用retain的拥有者的数量,如果对象没有拥有者,它就会被释放
Core Foundation 为对象的拥有和释放定义了以下的规则:
.如果你创建了对象(直接创建或者copy另外一个对象),你拥有这个对象
。如果你从某个地方获取一个对象,你不拥有它,如果你想阻止它被释放,你必须将你自己添加为一个owner(using CFRetain)
。如果你拥有一个对象,你必须在你使用完对象以后释放拥有关系(using CFRelease)

命名约定
使用Core Foundation时有很多方式可以得到一个对象的引用,你需要知道你是否从一个函数返回值拥有一个对象以便你知道如何考虑内存管理的细节。简短来说,如果函数名包括create,copy你就拥有这个对象。如果是包括单词get,你不拥有这个对象。

实例变量和传参数,原则就是:参数接收者为了防止接收的参数意外释放,接收者在使用前必须建立参数对象的拥有关系,使用完成后,释放关系(分别使用CFRetain和CFRelease)

示例
为了防止运行时错误和内存泄漏,你必须确定在接收,传递和返回对象时贯彻Core Foundation的拥有策略。
为了明白为什么成为一个不是你创建的对象的拥有者是必要的,可以思考以下这个问题。假设你从一个对象获取了一个值,如果这个值的容器对象释放了,它与容器对象的拥有关系就会被清除,如果这个容器对象是这个值的唯一拥有者,这个值就没有拥有者了,就会被释放了。现在你拥有一个释放对象的引用,当你尝试使用它的时候,你的应用就会crash。
static   CFStringRef  title =  NULL ;

void  SetTitle( CFStringRef  newTitle)
{
    
CFStringRef  temp =  title ;
    
title  =  CFStringCreateCopy ( kCFAllocatorDefault , newTitle);
    
CFRelease (temp);
}
这个地方有个tip,就是我们使用了copy来保持对象关系,而没有使用cfretain,是因为传递进来的是一个CFString,可能不希望被我们的函数修改,我们复制一个副本来使用,确保不修改原来的值。

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 );
    }
}


Core Foundation对象的生命周期管理
Core Foundation对象的的生命周期取决于它的引用计数-一个内部的计数用来表示谁希望保存该对象。当你创建或者复制一个对象时,它的引用计数设置为1,随后的客户可以通过CFRetain把计数增加1来关联这个对象关系。随后当你不需要用这个对象的时候,你可以使用CFRelease将引用计数减少1,当引用计数达到0时,对象的allocator就释放对象的内存。

Retaining对象的引用计数
为了增加Core Foundation的引用计数,把这个对象的引用当作参数传递给CFRetain函数
/* myString is a CFStringRef received from elsewhere */
myString = ( CFStringRef ) CFRetain ( myString );

Releasing对象的引用
为了减少Core Foundation的引用计数,把这个对象的引用当作参数传递给CFRelease函数
CFRelease(myString);

注意:你不能直接释放一个Core Foundation对象(例如调用free)。当你使用完一个对象,调用CFRelease函数,Core Foundation会正确的释放对象的。

如果你想知道Core Foundation 对象当前的引用计数,把这个对象的引用当作参数传递给CFGetRetainCount函数。
注意,事实上很少需要知道当前对象的引用计数,除非在调试时,如果你发现你需要知道一个对象retain的数量,检查你是否正确应用了关系策略规则。

复制函数
当你试图使用=来复制一个Core Foundation对象时,你需要注意你不会复制对象本身而且复制了对象的引用。
这种复制方式是很快的,因为仅仅是复制了引用地址,而没有复制对象,但是需要注意的是在可变对象上使用这种复制是很危险的。
如果你希望复制对象本身,你必须使用Core Foundation提供的函数来达到这个目的。比如CFString,你可以使用CFStringCreateCopy来创建一个与原始CFString对象完全相同的内容。Core Foundation类型同样提供了CreateMutableCopy,这个函数返回一个可以被修改的对象的副本。

浅拷贝和深拷贝
拷贝一个集合对象时候,集合对象里面包含其他的对象,需要多留心。和简单对象入CFString不同的是,createCopy函数提供给集合对象CFArray,CFSet实际执行的就是一个浅拷贝。浅拷贝就意味着集合对象创建了,但是集合里面的对象没有被复制,仅仅是对象的引用被复制到新的集合中去了。这种类型的复制是很有用的,当你处理不需要进行修改的对象时可以避免内存的浪费。

当你需要创建一个完整的集合对象时,你必须应用深拷贝。深拷贝复制集合对象同时复制集合对象里面的内容。
CFPropertyListCreateDeepCopy对一个属性列表执行深拷贝。
如果你想为其他对象创建深拷贝,你可以自己用递归下降进入到集合对象种复制所有的内容来执行深拷贝。实现这样的函数时要注意,集合对象可以递归,他们可能直接或者间接的包含他们自己的引用,这样会形成递归循环。

在创建函数中使用allocators。
每个创建函数都把allocator的引用当作第一个参数(CFAllocatorRef)。
作为参数的allocator,你有以下选择:
1. 你可以传递一个常量kCFAllocatorSystemDefault;这样指定了一般的allocator
2. 你可以传递一个NULL来指定当前默认的allocator。就相当于传递了kCFAllocatorDefault。
3. 你可以传递一个kCFAllocatorNull指出allocator不进行allocate,企图使用进行allocate的话会有一个error。有一些创建函数有一个参数为了特殊的allocator来重新分配或者释放内存。当这个参数指定为kCFAllocatorNull,你就阻止了自动再分配和释放。
4. 你可以使用CFGetAllocator从另外一个Core Foundation对象获取一个allocator的引用,然后当参数传递进去。这个计数允许你把相关的对象放进一个内存区域使用同样的allocator来分配他们。
5. 你也可以传递一个自定义的allocator的引用。 

如果你使用自定义的allocator并且你希望它成为默认的allocator。明智的做法时首先使用CFAllocatorGetDefault函数获取一个当前默认allocator的引用,然后存储到一个局部变量。当你完成使用你自定义的allocator后,使用CFAllocatorSetDefault函数重置存储的allocator为默认的allocator。

使用allocator context
在Core Foundation中每个allocator都有一个上下文。上下文是定义对象的操作环境和一些典型的函数指针的结构。
allocator的上下文由一个CFAllocatorContext结构体定义。除了函数指针,结构体包括了一些版本编号和用户自定义数据。
typedef   struct  {
    
CFIndex  version;
    
void  * info;
    
const   void  *(*retain)( const   void  *info);
    
void  (*release)( const   void  *info);
    
CFStringRef  (*copyDescription)( const   void  *info);
    
void  * (*allocate)( CFIndex  size,  CFOptionFlags  hint,  void  *info);
    
void  * (*reallocate)( void  *ptr,  CFIndex  newsize,  CFOptionFlags  hint,  void
                         *info);
    
void  (*deallocate)( void  *ptr,  void  *info);
    
CFIndex  (*preferredSize)( CFIndex  size,  CFOptionFlags  hint,  void  *info);
} CFAllocatorContext;
在当前的版本中,不要把version值设置为0以外的值。

下面的代码为获取分配器上下文和用户自定义数据提供的方法。
static   int  numOutstandingAllocations( CFAllocatorRef  alloc)
{
    
CFAllocatorContext  context;
    context.
version  =  0 ;
    
CFAllocatorGetContext (alloc, &context);
    
return  (*( int *)context. info );
}

创建自定义的allocators
为了创建自己的allocator,首先声明和初始化一个CFAllocatorContext的结构体。初始化版本为0,分配和赋值需要的数据,比如控制信息给info。
一旦你已经给CFAllocatorContext各个域赋予了合适的值,调用CFAllocatorCreate函数来创建一个allocator对象。这个函数的第二个参数是一个指向结构体的指针。这个函数的第一个参数定义了一个用来为新对象分配内存的allocator。如果你想用allocatCFAllocatteContext结构体中的回调函数,指定第一个参数kCFAllocatorUseContext常量。如果你打算使用默认的allocator,指定NULL。

static   CFAllocatorRef  myAllocator( void ) {
    
static   CFAllocatorRef  allocator =  NULL ;
    
if  (!allocator) {
        
CFAllocatorContext  context =
        {
0 NULL NULL , ( void  *)free,  NULL ,
            myAlloc, myRealloc, myDealloc, 
NULL };
        context.
info  =  malloc ( sizeof ( int ));
        allocator = 
CFAllocatorCreate ( NULL , &context);
    }
    
return  allocator;
}

实现allocator的回调函数
CFAllocatorContext结构体由7个定义回调函数的域。如果你创建一个自定义的allocator,你必须至少实现allocate函数,allocator回调函数必须是线程安全的并且,当回调函数调用其他函数时候,他们必须是可重入的。(可重入,简单来说就是不依赖传入参数和全局变量的函数)

retain,release,copyDescription等回调都把CFAllocatorContext的info当作他们的参数。void*,可以指向 你为allocator定义的 任何数据,如包含控制信息的结构体。

void  * (*allocate)( CFIndex  size,  CFOptionFlags  hint,  void  *info);
分配一块至少是size大小的内存,返回一个指向内存起始位置的指针。hint参数是你目前不能使用的位域。size参数通常必须大于0.如果这个参数不大于0,或者分配内存时候出错了,返回NULL。这个回调一般不是NULL.

你可能感兴趣的:(iOS,dev)