【问题标题】:Convert Unmanaged<AnyObject>! to Bool in Swift转换非托管<AnyObject>!在 Swift 中使用布尔值
【发布时间】:2015-10-08 13:56:03
【问题描述】:

我正在尝试获取现有 Objective C 类的方法的结果,该方法使用 performSelector

调用
let result = controlDelegate.performSelector("methodThatReturnsBOOL") as? Bool

我需要将此结果转换为 Swift 的 Bool 类型。

上面提到的代码,给出了一个警告

“从'Unmanaged!'到不相关的类型 'Bool' 总是失败”

结果总是“假”,即使方法返回 YES。

将结果转换为 Bool 有什么建议吗?

methodThatReturnsBOOL 的签名

- (BOOL)methodThatReturnsBOOL
{
    return YES;
}

【问题讨论】:

  • 可以发methodThatReturnsBOOL的签名吗?
  • 您不能将 performSelector() 与返回 BOOL 的方法一起使用。从文档中:“对于返回对象以外的任何方法,请使用 NSInvocation。” 但是 NSInvocation 在 Swift 中不可用(据我所知)。
  • @Alladinian : 问题已更新(它只是一个简单的 Objective C 方法,返回 YES/NO)
  • @MartinR :是的,我查看了 NSInvocation 并且它在 Swift 中不可用(还)。虽然 performSelector() 工作正常,但这只是如何捕获返回的 BOOL 值的问题。关于如何实现这一目标的任何其他建议?
  • 我们在现有的 Objective C 类中有一个 id 类型的委托。我们需要通过一个 Swift 类在这个委托上 performSelector,而后者又是现有 Objective C 类的子类。

标签: ios swift


【解决方案1】:

这个问题已经很久没有答案了,所以我正在添加我在此过程中学到的东西。

要转换由 Objective C 方法返回的 BOOL 值,您可以简单地使用它进行转换,

if let result = controlDelegate.performSelector("methodThatReturnsBOOL") {
    print("true")
} else {
    print("false")
}

如果需要,您还可以在此处将值 true/false 分配给 Swift Bool

注意:我尝试使用takeRetainedValue()Unmanaged&lt;AnyObject&gt; 直接转换为Bool,正如许多关于SO 的答案所建议的那样,但这似乎在这种情况下不起作用。

【讨论】:

  • 在调用中缺少参数“withObject”、“afterDelay”的参数
【解决方案2】:

这是 Swift 3 中的解决方案,因为方法有点不同。此方法还检查 Objective-C 对象是否响应选择器,以避免崩溃。

import ObjectiveC

let selector = NSSelectorFromString("methodThatReturnsBOOL")

guard controlDelegate.responds(to: selector) else {
    return
}

if let result = controlDelegate.perform(selector) {
    print("true")
}
else {
    print("false")
}

【讨论】:

    【解决方案3】:

    你不能在 Swift 中很好地做你想做的事。我对已接受的解决方案的问题是,它利用了 0x0 恰好nil 的想法。 Swift 和 Objective-C 规范实际上并不能保证这一点。这同样适用于布尔值,因为 0x0 为假而 0x1 为真只是一个任意的实现决策。除了在技术上不正确之外,它也是很难理解的代码。无需考虑大多数平台上的 nil 指针是什么(32/64 位零),所建议的内容就是零意义。

    在与 WWDC '19 的工程师交谈了一段时间后,他建议您实际上可以使用 valueFor(forKey:),键是函数名称/选择器描述。这是有效的,因为 Obj-C 运行时实际上将执行具有给定名称/键的任何函数以评估表达式。这仍然有点hacky,因为它需要Objective-C运行时的知识,但是它保证是平台和实现独立的,因为valueFor(forKey:)返回一个Any?,它可以转换成IntBool没有任何麻烦。通过使用内置转换而不是推测 0x0 或 0x1 的含义,我们避免了解释 nil 指针的整个问题。

    例子:

    @objc func doThing() -> Bool{
        return true
    }
    

    ...

    let target = someObjectWithDoThing
    let selectorCallResult = target.value(forKey: "doThing")
    
    let intResult = selectorCallResult as? Int //Optional<Int(1)>
    let boolResult = selectorCallResult as? Bool //Optional<Bool(true)>
    

    【讨论】:

      【解决方案4】:

      与我的回答 here 类似,这可以用 @convention(c) 代替:

      let selector: Selector = NSSelectorFromString("methodThatReturnsBOOL")
      let methodIMP: IMP! = controlDelegate.method(for: selector)
      let boolResult: Bool = unsafeBitCast(methodIMP,to:(@convention(c)(Any?,Selector)->Bool).self)(controlDelegate,selector)
      

      这种^ 特定语法从 Swift 3.1 开始可用,也可以在 Swift 3 中使用一个额外的变量。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-07
        • 1970-01-01
        • 2017-03-07
        • 2013-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多