转换非托管<AnyObject>!在 Swift 中使用布尔值

Posted

技术标签:

【中文标题】转换非托管<AnyObject>!在 Swift 中使用布尔值【英文标题】:Convert Unmanaged<AnyObject>! to Bool in 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 类的子类。 【参考方案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 中使用一个额外的变量。

【讨论】:

以上是关于转换非托管<AnyObject>!在 Swift 中使用布尔值的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 中使用 JSON 的 Alamofire 请求后,如何在 AnyObject 中转换 <AnyObject> 响应?

从 'Result<AnyObject>' 转换为不相关类型 'NSArray' 总是失败

如何将 Array<AnyObject> 转换为空数组变量?

错误:无法将 [anyobject] 转换为强制类型数组<_>

Alamofire 库 Void 不能转换为 Response<AnyObject, NSError> -> Void

无法将 Promise (_,_) -> DataRequest 类型的返回表达式转换为返回类型 Promise<DataResponse,AnyObject>>