CFDictionary 不会桥接到 NSDictionary (Swift 2.0 / iOS9)

CFDictionary won't bridge to NSDictionary (Swift 2.0 / iOS9)

好的,这是我在使用 CGImageSource 时遇到的一个案例,我注意到 CFDictionary 和 NSDictionary 之间的免费桥接在某些情况下似乎 运行 会出现问题。我设法构建了以下示例来说明我的意思:

func optionalProblemDictionary() -> CFDictionary? {
    let key = "key"
    let value = "value"
    var keyCallBacks = CFDictionaryKeyCallBacks()
    var valueCallBacks = CFDictionaryValueCallBacks()

    let cfDictionary = CFDictionaryCreate(kCFAllocatorDefault, UnsafeMutablePointer(unsafeAddressOf(key)), UnsafeMutablePointer(unsafeAddressOf(value)), 1, &keyCallBacks, &valueCallBacks)
    return cfDictionary
}

相当简单(也有点傻),但它是一个函数返回和可选的 CFDictionary。 "fun" 在尝试从此函数创建 NSDictionary 时启动:

为什么下面的方法不起作用?

if let problemDictionary = optionalProblemDictionary() as? NSDictionary {
    print(problemDictionary) // never enters, no warnings, compiles just fine
}

虽然这很好用?

if let cfDictionary = optionalProblemDictionary() {
    let problemDictionary = cfDictionary as NSDictionary
    print(problemDictionary)
}

XCode 7.0 (7A220)

原因似乎是函数 returns 一个 optional CFDictionary? 并且不能转换为(非可选) NSDictionary.

这是一个更简单的示例,展示了 CFStringNSString 的相同问题:

let cfString = "foobar" as CFString?

if let s1 = cfString as? NSString {
    print("s1 = \(s1)") // not executed
}

(问题仍然是为什么这不会给出编译器错误或 至少是一个编译器警告,因为这个可选的转换可以 从未成功。)

但是转换为可选的 NSString? 是可行的:

if let s2 = cfString as NSString? {
    print("s2 = \(s2)") // prints "s2 = foobar"
}

在您的情况下,如果将 "problematic case" 更改为

if let problemDictionary = cfDict as NSDictionary? {
    print(problemDictionary)
}

然后执行 if 块。


请注意,您在 Swift 中构建 CFDictionary 的方法不正确 实际上在我的测试中导致了程序崩溃。一个原因是 字典回调设置为空结构。 另一个问题是 unsafeAddressOf(key) 桥接 Swift 字符串到可以立即释放的 NSString

我不知道在 Swift 中构建 CFDictionary 的最佳方法是什么, 但这在我的测试中有效:

func optionalProblemDictionary() -> CFDictionary? {

    let key = "key" as NSString 
    let value = "value" as NSString

    var keys = [ unsafeAddressOf(key) ]
    var values = [ unsafeAddressOf(value) ]

    var keyCallBacks = kCFTypeDictionaryKeyCallBacks
    var valueCallBacks = kCFTypeDictionaryValueCallBacks

    let cfDictionary = CFDictionaryCreate(kCFAllocatorDefault, &keys, &values, 1, &keyCallBacks, &valueCallBacks)
    return cfDictionary
}