首页 » iOS编程基础:Swift、Xcode和Cocoa入门指南 » iOS编程基础:Swift、Xcode和Cocoa入门指南全文在线阅读

《iOS编程基础:Swift、Xcode和Cocoa入门指南》12.10 CFTypeRefs的内存管理

关灯直达底部

CFTypeRef纯粹是C中与Objective-C对象的等价之物。它是指向某个实现“细节未知”的C结构体的指针(参见附录A),“细节未知”指的是该结构体没有可直接访问的组件。这个结构体作为一个假对象;CFTypeRef等价于对象类型。不过,它并不是对象类型,处理CFTypeRef的代码也不是面向对象的;CFTypeRef没有属性和方法,你也不能向其发送任何消息。可以完全通过全局函数来使用CFTypeRefs,它们实际上是C函数。

在Objective-C中,CFTypeRef类型的特性是名字以Ref后缀结尾。不过在Swift中已经去掉了这个Ref后缀。

下面是用于绘制渐变色的Swift代码:


let con = UIGraphicsGetCurrentContext!let locs : [CGFloat] = [ 0.0, 0.5, 1.0 ]let colors : [CGFloat] = [    0.8, 0.4, // starting color, transparent light gray    0.1, 0.5, // intermediate color, darker less transparent gray    0.8, 0.4, // ending color, transparent light gray]let sp = CGColorSpaceCreateDeviceGraylet grad =    CGGradientCreateWithColorComponents (sp, colors, locs, 3)CGContextDrawLinearGradient (    con, grad, CGPointMake(89,0), CGPointMake(111,0), )  

在上述代码中,con是个CGContextRef,在Swift中则是CGContext;sp是个CGColorSpaceRef,在Swift中则是CGColorSpace;grad是个CGGradientRef,在Swift中则是CGGradient。它们都是CFTypeRefs。其代码并不是面向对象的;而是对全局C函数的一系列调用。

不过,CFTypeRef假对象也是个对象。这意味着被指针指向的C结构体实际上与引用所指向的类实例是一样的。这也意味着我们需要管理CFTypeRefs的内存。特别地,CFTypeRef假对象也有一个保持计数!其使用方式与真实对象别无二致,也遵循着内存管理的黄金法则。如果其所有者希望它一直存在,那么CFTypeRef就必须要保持;当所有者不再需要它时,我们得将其释放。

在Objective-C中,对于CFTypeRefs使用的黄金法则就是,如果通过一个函数(其名字包含了单词Create或Copy)获得了一个CFTypeRef对象,那么其保持计数就会增加。此外,如果担心对象的存活时间,那就需要显式调用CFRetain函数增加其保持计数来保持它。要想平衡Create、Copy与CFRetain调用,最终需要释放对象。在默认情况下,这是通过调用CFRelease函数实现的;不过,一些CFTypeRefs拥有自己专门的对象释放函数。比如,CGPath就有一个专门的CGPathRelease函数。

不过在Swift中,你永远不需要调用CFRetain或任何形式的CFRelease;事实上,你也无法调用。Swift会在背后自动调用。

可以将CFTypeRefs看作存在于两个世界中:纯C的CFTypeRef世界,以及面向对象内存管理的Swift世界。在获取到一个CFTypeRef假对象时,它会从CFTypeRef世界来到Swift世界。从那时起直到使用完,你都需要管理其内存。Swift知道这一点,大多数情况下,Swift本身都会使用黄金法则并进行正确的内存管理。这样,上面展示的绘制渐变色的代码实际上就会正确地管理好内存。在Objective-C中,我们需要释放sp与grad,因为它们是通过Create调用创建的;不这么做就会导致内存泄漏。不过在Swift中就没这个必要了,因为它会帮我们做这些事情。

在Swift中使用CFTypeRefs要比在Objective-C中简单。在Swift中,你可以将CFTypeRef假对象看作真实对象!比如,你可以在Swift中将CFTypeRef赋给属性,或将其作为参数传递给Swift函数,其内存会得到正确的管理;在Objective-C中,这些事情都很棘手。

不过,你可能会通过一些缺乏内存管理信息的API来接收CFTypeRef。这样的值应该引起你的注意,因为它会以包装了实际CFTypeRef的非托管值的形式进入Swift中。这会产生一个警告,因为Swift并不知道如何对这个假对象进行内存管理。实际上,只有通过调用Unmanaged对象的takeRetainedValue或takeUnretainedValue方法展开CFTypeRef后才能继续。你需要通过这两个方法来告诉Swift该如何正确管理这个对象的内存。对于通过名字中带有Create或Copy的内建函数所返回的CFTypeRef来说,请调用takeRetainedValue;否则请调用takeUnretainedValue。