【问题标题】:Swift - Checking unmanaged address book single value property for nilSwift - 检查非托管地址簿单值属性是否为零
【发布时间】:2014-11-18 01:05:51
【问题描述】:

我对 iOS 开发和 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 ""
   }
}()

contactReference 是ABRecordRef! 类型的变量

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

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

【问题讨论】:

    标签: ios swift unmanaged abaddressbook abrecordcopyvalue


    【解决方案1】:

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

    我已经定义了一个自定义操作符:

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

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

    funcA(funcB(param))
    

    变成

    param >>> funcB >>> funcA
    

    然后我使用这个函数将Unmanaged&lt;T&gt; 转换为 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,它接受一个可选的非托管,解包,检索保留值作为可选,解包,最后尝试转换为目标类型,即@987654333 @ 为名字属性。类型推断负责弄清楚 T 和 V 是什么:T 是包装在 Unmanaged 中的类型,V 是返回类型,这是已知的,因为在声明目标变量 let firstName: String? = ... 时指定。

    我想您已经仔细检查并请求用户访问通讯簿的权限。

    【讨论】:

    • 感谢安东尼奥的详细解答!看到您处理它的灵活且可重用的方式也很棒。 extractUnmanaged() 函数的略微修改版本(没有泛型)解决了我的问题。
    • 非通用版本是我的初稿,然后我不得不转换不同的类型,我想出了基于通用的解决方案和所有其他东西。顺便说一句,很高兴它帮助了你
    【解决方案2】:

    我在这里通过这个函数得到它:

    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 ""
        }
    }()
    

    【讨论】:

    • 为什么要投反对票?如果你不说有什么问题,我就无法纠正?
    • 感谢您的回答。很高兴看到这么多帮助!顺便说一句,我没有投反对票。什么意思?
    • @Rob - 我不是想说是你,但有人否决了我的回答。我很想知道为什么,因为这是我正在使用的并且对我有用。
    【解决方案3】:

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

    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 ?? ""
    

    【讨论】:

    • 我喜欢你的第二个版本。谢谢你!
    • 几天后,这就是我一直在寻找的答案!!非常感谢您的分享!
    • 当档案直接部署到手机中时访问没有名字/姓氏的记录时,这仍然对我不起作用,但最终使用 NSString 代替并使用可选绑定,如图所示 here
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-23
    • 1970-01-01
    • 1970-01-01
    • 2017-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多