97

The book says that "functions and closures are reference types". So, how do you find out if the references are equal? == and === don't work.

func a() { }
let å = a
let b = å === å // Could not find an overload for === that accepts the supplied arguments
  • 5
    As far as I can tell, you also can't check equality of metaclasses (e.g., `MyClass.self`) – Jiaaro Jun 09 '14 at 00:55
  • It shouldn't be necessary to compare two closures for identity. Can you give an example of where you would do this? There might be an alternative solution. – Bill Jun 09 '14 at 12:08
  • 1
    Multicast closures, a la C#. They're necessarily uglier in Swift, because you can't overload the (T, U) "operator", but we can still create them ourselves. Without being able to remove closures from an invocation list by reference, however, we need to create our own wrapper class. That is a drag, and shouldn't be necessary. –  Jun 09 '14 at 15:42
  • Id doesn't have address or anything like we had in Objective-C. It seems impossible for now unless you assign and id to your method and ovverride `===` operator. I don't know if you want to do this. – Gokhan Arik Jun 09 '14 at 21:38
  • 2
    Great question, but totally separate thing: your use of a diacritic on `å` to reference `a` is really interesting. Is there a convention you're exploring here? (I don't know if I actually like it or not; but it seems like it could be very powerful, especially in pure functional programming.) – Rob Napier Jun 11 '14 at 12:50
  • I used it because I'm excited to be able to use more characters, in Swift, and it made some kind of sense to me, to hold down 'option' and type 'a', for the above. (Option-B doesn't compile.) I doubt we need a convention, for what I did, in production code. If you're going to, for example, use an initializer that takes a function as a parameter, you probably want the property you assign the parameter to, to have the same name as the function, not an altered one. However, it might be nice to adopt some conventions just for clarity and brevity on Stack Overflow. –  Jun 11 '14 at 15:54
  • 3
    @Bill I am storing closures in an Array and can't use indexOf({$0 == closure} in order to find and remove them. Now I have to restructure my code due to optimization which I believe to be poor language design. – Zack Morris Jan 20 '16 at 22:50
  • @ZackMorris, I have a wrapped dictionary that I'm using to get around this described in my question here: http://stackoverflow.com/questions/37155530/how-to-make-a-collection-of-blocks-in-swift – Omegaman May 12 '16 at 04:04

11 Answers11

86

Chris Lattner wrote on the developer forums:

This is a feature we intentionally do not want to support. There are a variety of things that will cause pointer equality of functions (in the swift type system sense, which includes several kinds of closures) to fail or change depending on optimization. If "===" were defined on functions, the compiler would not be allowed to merge identical method bodies, share thunks, and perform certain capture optimizations in closures. Further, equality of this sort would be extremely surprising in some generics contexts, where you can get reabstraction thunks that adjust the actual signature of a function to the one the function type expects.

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

This means that you should not even try to compare closures for equality because optimizations may affect the outcome.

drewag
  • 93,393
  • 28
  • 139
  • 128
  • 25
    This just bit me, which was kind of devastating because I had been storing closures in an Array and now can't remove them with indexOf({$0 == closure} so I have to refactor. IMHO optimization shouldn't influence language design, so without a quick fix like the now deprecated @objc_block in matt's answer, I would argue that Swift can't properly store and retrieve closures at this time. So I don't think it's appropriate to advocate the use of Swift in callback heavy code like the kind encountered in web development. Which was the whole reason we switched to Swift in the first place... – Zack Morris Jan 20 '16 at 23:12
  • 5
    @ZackMorris Store some sort of identifier with the closure so you can remove it later. If you are using reference types you can just store a reference to the object otherwise you can come up with your own identifier system. You could even design a type that has a closure and a unique identifier that you can use instead of a plain closure. – drewag Jan 21 '16 at 05:36
  • 5
    @drewag Yeah, there are workarounds, but Zack is right. This is really really lame. I understand wanting to have optimizations, but if there is somewhere in code that the developer needs to compare some closures, then simply have the compiler not optimize those particular sections. Or make some kind of additional function of the compiler that enables it to create equality signatures that don't break with freaking optimizations. This is Apple we're talking about here... if they can fit a Xeon into an iMac then they can certainly make closures comparable. Give me a break! – CommaToast Jul 24 '17 at 09:21
  • 1
    @CommaToast Where do you keep the references to those closures so that you can remove them from the array later? Or are you instantiating the same closure again to remove it from the array? Would a value type conforming to `Hashable` with the same information as the closure work for you (it could implement `callAsFunction()`)? With that approach, instances could even be removed from the array without having to store them in another place and instead re-creating them. – Feuermurmel Aug 07 '21 at 11:07
11

I searched a lot. There seems to be no way of function pointer comparison. The best solution I got is to encapsulate the function or closure in an hashable object. Like:

var handler:Handler = Handler(callback: { (message:String) in
            //handler body
}))
tuncay
  • 111
  • 1
  • 3
  • 2
    This is, by far, the best approach. It sucks to have to wrap and unwrap closures, but it's better than nondeterministic, unsupported fragility. –  Jul 03 '16 at 05:22
10

Simplest way is designate the block type as @objc_block, and now you can cast it to an AnyObject which is comparable with ===. Example:

    typealias Ftype = @convention(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

Update 2021; changed @objc_block to @convention(block) to support Swift 2.x and later (which don't recognize @objc_block).

Top-Master
  • 7,611
  • 5
  • 39
  • 71
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 1
    Hey , I am trying if unsafeBitCast(listener, AnyObject.self) === unsafeBitCast(f, AnyObject.self) but get fatal error: can't unsafeBitCast between types of different sizes. The idea is to build a event based system but the removeEventListener method should be able to check the function pointers. – freezing_ Jan 28 '15 at 21:19
  • 3
    Use @convention(block) instead of @objc_block on Swift 2.x. Great answer! – Gabriel.Massana Jun 15 '16 at 10:56
5

I've been looking for the answer, too. And I've found it at last.

What you need is the actual function pointer and its context hidden in the function object.

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
}

And here is the demo:

// 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)")

See the URLs below to find why and how it works:

As you see it is capable of checking identity only (the 2nd test yields false). But that should be good enough.

dankogai
  • 1,627
  • 12
  • 7
  • 6
    This method will not be reliable with compiler optimizations https://devforums.apple.com/message/1035180#1035180 – drewag Sep 05 '14 at 21:15
  • 9
    This is an hack based on undefined implementation details. Then using this means your program will produce an undefined result. – eonil Nov 09 '14 at 20:10
  • 9
    Note that this relies on undocumented stuff, and undisclosed implementation details, which can crash your app in future in they change. Not recommended to be used in production code. – Cristik Jan 23 '16 at 21:29
  • This is "clover", but completely unworkable. I don't know why this was rewarded a bounty. The language *intentionally* doesn't have function equality, for the exact purpose of freeing the compiler to *break function equality freely* in order to yield better optimizations. – Alexander Jan 03 '19 at 04:44
  • ... and this is exactly the approach Chris Lattner advocates against (see the top answer). – pipacs Mar 06 '19 at 14:45
4

Here is one possible solution (conceptually the same as 'tuncay' answer). The point is to define a class that wraps some functionality (e.g. Command):

Swift:

typealias Callback = (Any...)->Void
class Command {
    init(_ fn: @escaping Callback) {
        self.fn_ = fn
    }

    var exec : (_ args: Any...)->Void {
        get {
            return fn_
        }
    }
    var fn_ :Callback
}

let cmd1 = Command { _ in print("hello")}
let cmd2 = cmd1
let cmd3 = Command { (_ args: Any...) in
    print(args.count)
}

cmd1.exec()
cmd2.exec()
cmd3.exec(1, 2, "str")

cmd1 === cmd2 // true
cmd1 === cmd3 // false

Java:

interface Command {
    void exec(Object... args);
}
Command cmd1 = new Command() {
    public void exec(Object... args) [
       // do something
    }
}
Command cmd2 = cmd1;
Command cmd3 = new Command() {
   public void exec(Object... args) {
      // do something else
   }
}

cmd1 == cmd2 // true
cmd1 == cmd3 // false
baso
  • 41
  • 4
3

This is a great question and while Chris Lattner intentionally doesn't want to support this feature I, like many developers, also can't let go of my feelings coming from other languages where this is a trivial task. There are plenty of unsafeBitCast examples, most of them don't show the full picture, here's a more detailed one:

typealias SwfBlock = () -> ()
typealias ObjBlock = @convention(block) () -> ()

func testSwfBlock(a: SwfBlock, _ b: SwfBlock) -> String {
    let objA = unsafeBitCast(a as ObjBlock, AnyObject.self)
    let objB = unsafeBitCast(b as ObjBlock, AnyObject.self)
    return "a is ObjBlock: \(a is ObjBlock), b is ObjBlock: \(b is ObjBlock), objA === objB: \(objA === objB)"
}

func testObjBlock(a: ObjBlock, _ b: ObjBlock) -> String {
    let objA = unsafeBitCast(a, AnyObject.self)
    let objB = unsafeBitCast(b, AnyObject.self)
    return "a is ObjBlock: \(a is ObjBlock), b is ObjBlock: \(b is ObjBlock), objA === objB: \(objA === objB)"
}

func testAnyBlock(a: Any?, _ b: Any?) -> String {
    if !(a is ObjBlock) || !(b is ObjBlock) {
        return "a nor b are ObjBlock, they are not equal"
    }
    let objA = unsafeBitCast(a as! ObjBlock, AnyObject.self)
    let objB = unsafeBitCast(b as! ObjBlock, AnyObject.self)
    return "a is ObjBlock: \(a is ObjBlock), b is ObjBlock: \(b is ObjBlock), objA === objB: \(objA === objB)"
}

class Foo
{
    lazy var swfBlock: ObjBlock = self.swf
    func swf() { print("swf") }
    @objc func obj() { print("obj") }
}

let swfBlock: SwfBlock = { print("swf") }
let objBlock: ObjBlock = { print("obj") }
let foo: Foo = Foo()

print(testSwfBlock(swfBlock, swfBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testSwfBlock(objBlock, objBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false

print(testObjBlock(swfBlock, swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testObjBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true

print(testAnyBlock(swfBlock, swfBlock)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true

print(testObjBlock(foo.swf, foo.swf)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testSwfBlock(foo.obj, foo.obj)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testAnyBlock(foo.swf, foo.swf)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(foo.swfBlock, foo.swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true

The interesting part is how swift freely casts SwfBlock to ObjBlock, yet in reality two casted SwfBlock blocks will always be different values, while ObjBlocks won't. When we cast ObjBlock to SwfBlock, the same thing happens to them, they become two different values. So, in order to preserve the reference, this sort of casting should be avoided.

I'm still comprehending this whole subject, but one thing I left wishing for is ability to use @convention(block) on class / struct methods, so I filed a feature request that needs up-voting or explaining why it's a bad idea. I also get a sense this approach might be bad all together, if so, can anyone explain why?

Ian Bytchek
  • 8,804
  • 6
  • 46
  • 72
  • 1
    I don't think you understand Chris Latner's reasoning as to why this isn't (and shouldn't be) supported. " I also get a sense this approach might be bad all together, if so, can anyone explain why?" Because in an optimized build, the compiler is free to mangle the code in many ways that break the idea of point equality of functions. For a basic example, if one function's body starts the same way another function does, the compiler is likely to overlap the two in the machine code, only keeping different exit points. This reduces duplication – Alexander Jan 03 '19 at 04:33
  • 1
    Fundamentally, closures are ways of initiating objects of anonymous classes (just like in Java, but there's it's more obvious). These closures objects are heap allocated, and store the data captured by the closure, which act like implicit parameter's to the closure's function. The closure object holds a reference to a function which operates upon the explicit (via func args) and implicit (via captured closure context) args. While the function body can be shared as a single unique point, the closure object's pointer *can't* be, because there's one closure object per set of enclosed values. – Alexander Jan 03 '19 at 04:37
  • 1
    So when you have `Struct S { func f(_: Int) -> Bool }`, you actually have a function of type `S.f` which has type `(S) -> (Int) -> Bool`. This function *can* be shared. It is solely parameterized by its explicit parameters. By when you use it as an instance method (either by implicitly binding the `self` parameter by calling the method on an object, e.g. `S().f`, or by explicitly binding it, e.g. `S.f(S())`), you create a new closure object. This object stores a pointer to `S.f` (which can be shared)`, but also to your instance (`self`, the `S()`). – Alexander Jan 03 '19 at 04:41
  • 1
    This closure object must be unique per instance of `S`. If closure pointer equality were possible, then you would be surprised to discover that `s1.f` is not the same pointer as `s2.f` (because one is a closure object which references `s1` and `f`, and the other is a closure object which references `s2` and `f`). – Alexander Jan 03 '19 at 04:42
  • This is brilliant, thank you! Yes, by now I had a picture of what's going on and this puts everything into a perspective! – Ian Bytchek Jan 03 '19 at 07:51
3

Not a general solution, but if one is trying to implement a listener pattern, I've ended up returning an "id" of the function during the registration so I can use it to unregister later (which is kind of workaround to the original question for "listeners" case as usually unregistering comes down to checking the functions for equality, which is at least not "trivial" as per other answers).

So something like this:

class OfflineManager {
    var networkChangedListeners = [String:((Bool) -> Void)]()

    func registerOnNetworkAvailabilityChangedListener(_ listener: @escaping ((Bool) -> Void)) -> String{
        let listenerId = UUID().uuidString;
        networkChangedListeners[listenerId] = listener;
        return listenerId;
    }
    func unregisterOnNetworkAvailabilityChangedListener(_ listenerId: String){
        networkChangedListeners.removeValue(forKey: listenerId);
    }
}

Now you just need to store the key returned by the "register" function and pass it when unregistering.

vir us
  • 9,920
  • 6
  • 57
  • 66
  • Thank you for great answer! It seems to be the easiest way to hack that swift can not compare function references. I have implemented simple prop `private var listenerId = 0` and increments it and returns when reregistering listener to avoid komplex `UUID().uuidString`. – mikep Feb 26 '21 at 12:33
2

Well it's been 2 days and nobody has chimed in with a solution, so I'll change my comment to an answer:

As far as I can tell, you can't check equality or identity of functions (like your example) and metaclasses (e.g., MyClass.self):

But – and this is just an idea – I can't help but notice that the where clause in generics appears to be able to check equality of types. So maybe you can leverage that, at least for checking identity?

Jiaaro
  • 74,485
  • 42
  • 169
  • 190
0

My solution was to wrap functions to class that extends NSObject

class Function<Type>: NSObject {
    let value: (Type) -> Void

    init(_ function: @escaping (Type) -> Void) {
        value = function
    }
}
Renetik
  • 5,887
  • 1
  • 47
  • 66
  • When you do that, how do compare them? let's say you want to remove one of them from an array of your wrappers, how do you do that? Thanks. – Ricardo May 17 '20 at 11:32
0

I know I'm answering this question six years late, but I think it's worth looking at the motivation behind the question. The questioner commented:

Without being able to remove closures from an invocation list by reference, however, we need to create our own wrapper class. That is a drag, and shouldn't be necessary.

So I guess the questioner wants to maintain a callback list, like this:

class CallbackList {
    private var callbacks: [() -> ()] = []

    func call() {
        callbacks.forEach { $0() }
    }

    func addCallback(_ callback: @escaping () -> ()) {
        callbacks.append(callback)
    }

    func removeCallback(_ callback: @escaping () -> ()) {
        callbacks.removeAll(where: { $0 == callback })
    }
}

But we can't write removeCallback that way, because == doesn't work for functions. (Neither does ===.)

Here's a different way to manage your callback list. Return a registration object from addCallback, and use the registration object to remove the callback. Here in 2020, we can use the Combine's AnyCancellable as the registration.

The revised API looks like this:

class CallbackList {
    private var callbacks: [NSObject: () -> ()] = [:]

    func call() {
        callbacks.values.forEach { $0() }
    }

    func addCallback(_ callback: @escaping () -> ()) -> AnyCancellable {
        let key = NSObject()
        callbacks[key] = callback
        return .init { self.callbacks.removeValue(forKey: key) }
    }
}

Now, when you add a callback, you don't need to keep it around to pass to removeCallback later. There is no removeCallback method. Instead, you save the AnyCancellable, and call its cancel method to remove the callback. Even better, if you store the AnyCancellable in an instance property, then it will cancel itself automatically when the instance is destroyed.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • The most common reason we need this is to manage multiple subscribers for publishers. Combine solves that without all of this. What C# allows, and Swift doesn't, is to find out if two closures reference the same named function. That is also useful, but much less often. –  Aug 05 '20 at 17:59
0

You can use a callAsFunction method so for example

struct MyType: Equatable {
    func callAsFunction() {
        print("Image a function")
    }

    static func == (lhs: MyType, rhs: MyType) -> Bool { true }
}

let a = MyType()
let b = MyType()
a()
b()
let e = a == b

In this case they are always going to be true, you can have initialiser t give them different internal states, or other methods to change there states, and the callAsFunction can be changed to take arguments

Not sure why === would not work on real functions because you are just testing address, but == invokes the == method of the Equatable protocol, and function don't implement this protocol

Nathan Day
  • 5,981
  • 2
  • 24
  • 40