Swift - 检查nil的非托管地址簿单值属性

时间:2014-09-23 17:54:56

标签: ios swift unmanaged abaddressbook abrecordcopyvalue

我对iOS-Development和swift相对较新。但到目前为止,我总是能够通过对stackoverflow和一些文档和教程的一些研究来帮助自己。 但是,有一个问题我找不到任何解决方案。

我想从用户地址簿中获取一些数据(例如单值属性kABPersonFirstNameProperty)。因为如果此联系人在地址簿中没有firstName值,.takeRetainedValue()函数会抛出错误,我需要确保ABRecordCopyValue()函数确实返回一个值。我试图在一个闭包中检查这个:

let contactFirstName: String = {
   if (ABRecordCopyValue(self.contactReference, kABPersonFirstNameProperty) != nil) {
      return ABRecordCopyValue(self.contactReference, kABPersonFirstNameProperty).takeRetainedValue() as String
   } else {
      return ""
   }
}()

contactReferenceABRecordRef!

类型的变量

当地址簿联系人提供firstName值时,一切正常。但是如果没有firstName,则应用程序会被.takeRetainedValue()函数崩溃。似乎if语句没有帮助,因为ABRecordCopyValue()函数的非托管返回值不是nil,尽管没有firstName。

我希望我能够清楚地解决问题。如果有人能帮我解决一些脑波,那将是很棒的。

3 个答案:

答案 0 :(得分:32)

如果我想要与各种属性关联的值,我使用以下语法:

let first = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String
let last  = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String

或者您可以使用可选绑定:

if let first = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String {
    // use `first` here
}
if let last  = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String {
    // use `last` here
}

如果你真的想要返回一个非可选的,其中缺失的值是零长度字符串,你可以使用??运算符:

let first = ABRecordCopyValue(person, kABPersonFirstNameProperty)?.takeRetainedValue() as? String ?? ""
let last  = ABRecordCopyValue(person, kABPersonLastNameProperty)?.takeRetainedValue() as? String ?? ""

答案 1 :(得分:5)

我在这里通过这个功能得到了它:

func rawValueFromABRecordRef<T>(recordRef: ABRecordRef, forProperty property: ABPropertyID) -> T? {
    var returnObject: T? = nil
    if let rawValue: Unmanaged<AnyObject>? = ABRecordCopyValue(recordRef, property) {
        if let unwrappedValue: AnyObject = rawValue?.takeRetainedValue() {
            println("Passed: \(property)")
            returnObject = unwrappedValue as? T
        }
        else {
            println("Failed: \(property)")
        }
    }
    return returnObject
}

您可以在您的财产中使用它,如下所示:

let contactFirstName: String = {
    if let firstName: String = rawValueFromABRecordRef(recordRef, forProperty: kABPersonFirstNameProperty) {
        return firstName
    }
    else {
        return ""
    }
}()

答案 2 :(得分:1)

也许这不只是回答你的问题,但这就是我处理地址簿的方式。

我已定义了自定义运算符:

infix operator >>> { associativity left }
func >>> <T, V> (lhs: T, rhs: T -> V) -> V {
    return rhs(lhs)
}

允许以更易读的方式对函数进行多次调用,例如:

funcA(funcB(param))

变为

param >>> funcB >>> funcA

然后我使用此函数将Unmanaged<T>转换为swift类型:

func extractUnmanaged<T, V>(value: Unmanaged<T>?) -> V? {
    if let value = value {
        var innerValue: T? = value.takeRetainedValue()
        if let innerValue: T = innerValue {
            return innerValue as? V
        }
    }
    return .None
}

和使用CFArray的对方:

func extractUnmanaged(value: Unmanaged<CFArray>?) -> [AnyObject]? {
    if let value = value {
        var innerValue: CFArray? = value.takeRetainedValue()
        if let innerValue: CFArray = innerValue {
            return innerValue.__conversion()
        }
    }
    return .None
}

这是打开地址簿,检索所有联系人的代码,每个人都读取了第一个名字和组织(在模拟器中,firstName总是有一个值,而部门没有,所以它&#39;有利于测试):

let addressBook: ABRecordRef? = ABAddressBookCreateWithOptions(nil, nil) >>> extractUnmanaged
let results = ABAddressBookCopyArrayOfAllPeople(addressBook) >>> extractUnmanaged
if let results = results {
    for result in results {
        let firstName: String? = (result, kABPersonFirstNameProperty) >>> ABRecordCopyValue >>> extractUnmanaged
        let organization: String? = (result, kABPersonOrganizationProperty) >>> ABRecordCopyValue >>> extractUnmanaged
        println("\(firstName) - \(organization)")
    }
}

请注意,println语句会打印可选项,因此您将在控制台Optional("David")中看到而不只是David。当然这仅供演示。

回答你的问题的函数是extractUnmanaged,它接受​​一个可选的非托管,解包它,检索保留的值为可选,解包它,最后尝试转换为目标类型,这是String表示名字属性。类型推断负责计算T和V是什么:T是包含在Unmanaged中的类型,V是返回类型,这是已知的,因为在声明目标变量let firstName: String? = ...时指定。

我认为您已经负责检查并要求用户获得访问该地址簿的许可。