Rob*_*ins 6 casting unsafe-pointers swift swift3
我有一个技巧来帮助测试UIAlertController在Swift 2.x中工作:
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])
}
}
Run Code Online (Sandbox Code Playgroud)
这在Swift 3.x下失败了fatal error: can't unsafeBitCast between types of different sizes,这诱使我相信可能有一种方法可以让演员工作.任何人都可以搞清楚吗?
Rob*_*ins 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])
}
}
}
Run Code Online (Sandbox Code Playgroud)
(最初,该block值是实际的块,而不是指向块的指针 - 您显然无法将其转换为指针AlertHandler)
我的回答基于@Robert Atkins 的,但更短。这里的问题是,valueForKey返回一个Any类型化的对象,因为在 Swift 中,
MemoryLayout<Any>.size == 32
MemoryLayout<AnyObjcBlockType>.size == 8
Run Code Online (Sandbox Code Playgroud)
在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])
}
}
Run Code Online (Sandbox Code Playgroud)