12

我对 iOS 开发和 swift 比较陌生。但到目前为止,我总是能够通过对 stackoverflow 的一些研究以及一些文档和教程来帮助自己。但是,有一个问题我还没有找到任何解决方案。

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

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

contactReference是类型的变量ABRecordRef!

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

我希望我能把我的问题说清楚。如果有人能用一些脑电波帮助我,那就太好了。

4

3 回答 3

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 ?? ""
于 2014-09-24T04:00:18.293 回答
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 ""
    }
}()
于 2014-09-23T23:54:40.197 回答
1

也许这不仅仅是回答你的问题,但这就是我处理通讯录的方式。

我定义了一个自定义运算符:

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

允许以更易读的方式链接多个函数调用,例如:

funcA(funcB(param))

变成

param >>> funcB >>> funcA

然后我使用此函数将 an 转换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 总是有一个值,而部门没有,所以它有利于测试):

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? = ...

我想您已经注意检查并要求用户允许访问地址簿。

于 2014-09-23T19:05:05.913 回答