我有a trick来帮助测试在Swift 2.x中运行的UIAlertController
:
extension UIAlertController {
typealias AlertHandler = @convention(block) (UIAlertAction) -> Void
func tapButtonAtIndex(index: Int) {
let block = actions[index].valueForKey("handler")
let handler = unsafeBitCast(block, AlertHandler.self)
handler(actions[index])
}
}
这在Swift 3.x下使用fatal error: can't unsafeBitCast between types of different sizes
失败,这诱使我相信可能有一种方法可以使演员工作。有人能想出来吗?
答案 0 :(得分:19)
找到一个适用于Swift 3.0.1的解决方案
extension UIAlertController {
typealias AlertHandler = @convention(block) (UIAlertAction) -> Void
func tapButton(atIndex index: Int) {
if let block = actions[index].value(forKey: "handler") {
let blockPtr = UnsafeRawPointer(Unmanaged<AnyObject>.passUnretained(block as AnyObject).toOpaque())
let handler = unsafeBitCast(blockPtr, to: AlertHandler.self)
handler(actions[index])
}
}
}
(最初,block
值是实际的块,而不是指向块的指针 - 您显然无法将其转换为指向AlertHandler
的指针
答案 1 :(得分:3)
我的答案基于@Robert Atkins,但更短。
这里的问题是,valueForKey
返回一个Any
类型的对象,因为在Swift中,
MemoryLayout<Any>.size == 32
MemoryLayout<AnyObjcBlockType>.size == 8
当在不同大小的类型之间进行转换时,将在unsafeBitCast
中触发断言。
一种解决方法是创建一个中间包装器并转换回原始指针,满足MemoryLayout<UnsafeRawPointer>.size == 8
。
更简单的方法是使用协议AnyObject
直接创建间接引用,依赖MemoryLayout<AnyObject >.size == 8
这一事实,我们可以编写以下有效代码:
typealias AlertHandler = @convention(block) (UIAlertAction) -> Void
func tapButton(atIndex index: Int) {
if let block = actions[index].value(forKey: "handler") {
let handler = unsafeBitCast(block as AnyObject, to: AlertHandler.self)
handler(actions[index])
}
}
答案 2 :(得分:1)
如果您的UIAlertController是一个操作表,您可以修改Robert的答案,在执行处理程序之前解除UIAlertController。
dismiss(动画:true,完成:处理程序中的{()(self.actions [index])})
我正在使用这个扩展进行测试,如果没有这个修改,我提出的视图控制器的断言就失败了。