你如何测试函数和闭包是否相等?

Jes*_*ssy 83 closures equality swift

书中说"功能和闭包是参考类型".那么,你怎么知道引用是否相等?==和===不起作用.

func a() { }
let å = a
let b = å === å // Could not find an overload for === that accepts the supplied arguments
Run Code Online (Sandbox Code Playgroud)

以下是Catterwauls如何处理这个问题:

MultiClosures和Equatable Closures

测试

dre*_*wag 65

Chris Lattner在开发者论坛上写道:

这是我们有意不想支持的功能.有许多事情会导致函数的指针相等(在swift类型系统意义上,包括几种闭包)失败或根据优化而改变.如果在函数上定义了"===",则不允许编译器合并相同的方法体,共享thunk,并在闭包中执行某些捕获优化.此外,在某些泛型上下文中,这种相等性是非常令人惊讶的,在这些上下文中,您可以获得将函数的实际签名调整为函数类型所期望的重新抽取thunk.

https://devforums.apple.com/message/1035180#1035180

这意味着您甚至不应该尝试比较闭包是否相等,因为优化可能会影响结果.

  • 这只是一点点我,这是一种毁灭性的,因为我一直在存储数组中的闭包,现在不能用indexOf删除它们({$ 0 == closure}所以我必须重构.IMHO优化不应该影响语言设计,所以如果没有像在matt的答案中现在弃用的@objc_block那样快速修复,我会认为Swift目前无法正确存储和检索闭包.所以我认为在回调重代码中提倡使用Swift是不合适的.就像网络开发中遇到的那样.这就是我们首先转向Swift的全部原因...... (17认同)
  • @drewag是的,有解决方法,但Zack是对的.这真的很蹩脚.我理解想要进行优化,但如果代码中的某个地方开发人员需要比较一些闭包,那么只需让编译器不优化这些特定部分.或者创建编译器的某种附加功能,使其能够创建不会破坏优化的等同签名.这是我们在这里谈论的Apple ......如果他们能够将Xeon安装到iMac中,那么他们当然可以使封装具有可比性.给我一个休息! (4认同)
  • @ZackMorris使用闭包存储某种标识符,以便稍后将其删除.如果您使用的是引用类型,则可以只存储对象的引用,否则您可以使用自己的标识符系统.您甚至可以设计一个具有闭包和唯一标识符的类型,您可以使用它而不是普通闭包. (3认同)

mat*_*att 8

最简单的方法是将块类型指定为@objc_block,现在可以将其转换为与之相当的AnyObject ===.例:

    typealias Ftype = @objc_block (s:String) -> ()

    let f : Ftype = {
        ss in
        println(ss)
    }
    let ff : Ftype = {
        sss in
        println(sss)
    }
    let obj1 = unsafeBitCast(f, AnyObject.self)
    let obj2 = unsafeBitCast(ff, AnyObject.self)
    let obj3 = unsafeBitCast(f, AnyObject.self)

    println(obj1 === obj2) // false
    println(obj1 === obj3) // true
Run Code Online (Sandbox Code Playgroud)

  • 在Swift 2.x上使用@convention(block)而不是@objc_block.很棒的答案! (2认同)

小智 7

我搜索了很多.似乎没有办法比较函数指针.我得到的最好的解决方案是将函数或闭包封装在一个可散列的对象中.喜欢:

var handler:Handler = Handler(callback: { (message:String) in
            //handler body
}))
Run Code Online (Sandbox Code Playgroud)

  • 到目前为止,这是最好的方法.它不得不包装和解开封口,但它比不确定的,不受支持的脆弱性更好. (2认同)

dan*_*gai 6

我一直在寻找答案.我终于找到了它.

您需要的是实际的函数指针及其隐藏在函数对象中的上下文.

func peekFunc<A,R>(f:A->R)->(fp:Int, ctx:Int) {
    typealias IntInt = (Int, Int)
    let (hi, lo) = unsafeBitCast(f, IntInt.self)
    let offset = sizeof(Int) == 8 ? 16 : 12
    let ptr  = UnsafePointer<Int>(lo+offset)
    return (ptr.memory, ptr.successor().memory)
}
@infix func === <A,R>(lhs:A->R,rhs:A->R)->Bool {
    let (tl, tr) = (peekFunc(lhs), peekFunc(rhs))
    return tl.0 == tr.0 && tl.1 == tr.1
}
Run Code Online (Sandbox Code Playgroud)

这是演示:

// simple functions
func genericId<T>(t:T)->T { return t }
func incr(i:Int)->Int { return i + 1 }
var f:Int->Int = genericId
var g = f;      println("(f === g) == \(f === g)")
f = genericId;  println("(f === g) == \(f === g)")
f = g;          println("(f === g) == \(f === g)")
// closures
func mkcounter()->()->Int {
    var count = 0;
    return { count++ }
}
var c0 = mkcounter()
var c1 = mkcounter()
var c2 = c0
println("peekFunc(c0) == \(peekFunc(c0))")
println("peekFunc(c1) == \(peekFunc(c1))")
println("peekFunc(c2) == \(peekFunc(c2))")
println("(c0() == c1()) == \(c0() == c1())") // true : both are called once
println("(c0() == c2()) == \(c0() == c2())") // false: because c0() means c2()
println("(c0 === c1) == \(c0 === c1)")
println("(c0 === c2) == \(c0 === c2)")
Run Code Online (Sandbox Code Playgroud)

请参阅以下网址,了解原因及工作原理:

如您所见,它只能检查身份(第二次测试结果false).但这应该足够好了.

  • 请注意,这取决于未记录的内容以及未披露的实施细节,这些细节可能会在将来更改时使您的应用崩溃.不建议在生产代码中使用. (6认同)
  • 这是基于未定义的实现细节的hack.然后使用这意味着您的程序将产生未定义的结果. (5认同)
  • 编译器优化后此方法不可靠https://devforums.apple.com/message/1035180#1035180 (3认同)

Jia*_*aro 2

已经两天了,还没有人提出解决方案,所以我将我的评论更改为答案:

\n\n

据我所知,您无法检查函数(如您的示例)和元类(例如,MyClass.self)的相等性或同一性:

\n\n

但是 \xe2\x80\x93 这只是一个想法 \xe2\x80\x93 我不禁注意到where泛型中的子句似乎能够检查类型的相等性。那么也许您可以利用它,至少可以检查身份?

\n