将Swift中的Unmanaged<AnyObject>!转换为Bool

8

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

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

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

上面提到的代码会提示警告

"从 'Unmanaged!' 转换为无关的类型 'Bool' 总是失败"

而且即使该方法返回YES,结果始终为“false”。

有没有将结果转换为Bool的建议?

方法methodThatReturnsBOOL的签名

- (BOOL)methodThatReturnsBOOL
{
    return YES;
}

УЃйтљдтЈЉтИЃ methodThatReturnsBOOL уџёуГЙтљЇ№╝Ъ - Alladinian
你不能使用performSelector()调用返回BOOL的方法。根据文档:“对于返回对象以外的任何内容的方法,请使用NSInvocation。”然而,据我所知,Swift中没有NSInvocation可用。 - Martin R
@Alladinian:问题已更新(这只是一个简单的Objective C方法,返回YES/NO)。 - UditS
@MartinR:是的,我研究了NSInvocation,但它目前在Swift中不可用。 虽然performSelector()运行良好,只是需要找到如何捕获返回的BOOL值。 还有其他建议吗? - UditS
为什么你需要使用performSelector呢? - Martin R
1
我们在一个现有的Objective C类中有一个类型为id的委托。我们需要通过一个Swift类来执行这个委托的performSelector方法,而这个Swift类又是现有Objective C类的子类。 - UditS
5个回答

10

很长时间以来,这个问题一直没有答案,所以我会分享我学到的经验。

要将Objective C方法返回的BOOL值转换,您可以通过简单的强制转换来实现,例如:

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

在这里,如果需要的话,您也可以将true/false值分配给Swift Bool

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


在调用中缺少参数'withObject'和'afterDelay'。 - Zaporozhchenko Oleksandr

3
你无法在Swift中很好地完成你想要的事情。我对已接受的解决方案的问题在于它利用了0x0刚好是nil这个想法。然而,这在Swift和Objective-C规范中并不是保证的。同样适用于布尔值,因为0x0为false和0x1为true只是任意实现决策。除了技术上不正确外,这也是难以理解的可怕代码。如果没有考虑大多数平台上nil指针是什么(32/64位的零),建议的内容毫无意义。
在与WWDC '19的一位工程师交谈了一段时间后,他建议您可以使用键作为函数名/选择器描述,并使用valueFor(forKey:)。这可以正常工作,因为Obj-C运行时将实际执行具有给定名称/键的任何函数以评估表达式。这仍然有点黑客,因为它需要了解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)>

1

以下是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")
}

0

更紧凑的 bool 强制转换:

let result = controlDelegate.perform(NSSelectorFromString("methodThatReturnsBOOL")) != nil

0

类似于我在这里的回答,这可以使用@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中也可以使用一个额外的变量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接