Le livre dit que “les fonctions et les fermetures sont des types de référence”. Alors, comment savoir si les références sont égales? == et === ne fonctionnent pas.
func a() { } let å = a let b = å === å // Could not find an overload for === that accepts the supplied arguments
Voici comment les Catterwauls traitent ceci:
MultiClosures et fermetures équivoques
des tests
Chris Lattner a écrit sur les forums de développeurs:
C’est une fonctionnalité que nous ne voulons pas prendre en charge intentionnellement. Il y a toute une variété de choses qui font échouer ou changer les fonctions des pointeurs (dans le sens du système de type rapide, qui inclut plusieurs types de fermetures) en fonction de l’optimisation. Si “===” était défini sur des fonctions, le compilateur ne serait pas autorisé à fusionner des corps de méthodes identiques, à partager des fichiers thunks et à effectuer certaines optimisations de capture dans les fermetures. De plus, une égalité de ce genre serait extrêmement surprenante dans certains contextes génériques, où vous pouvez obtenir des thunks de réaffectation qui ajustent la signature réelle d’une fonction à celle attendue par le type de fonction.
https://devforums.apple.com/message/1035180#1035180
Cela signifie que vous ne devriez même pas essayer de comparer les fermetures pour l’égalité, car les optimisations peuvent affecter le résultat.
J’ai aussi cherché la réponse. Et je l’ai enfin trouvé.
Ce dont vous avez besoin, c’est du pointeur de fonction réel et de son contexte caché dans l’object fonction.
func peekFunc(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(lo+offset) return (ptr.memory, ptr.successor().memory) } @infix func === (lhs:A->R,rhs:A->R)->Bool { let (tl, tr) = (peekFunc(lhs), peekFunc(rhs)) return tl.0 == tr.0 && tl.1 == tr.1 }
Et voici la démo:
// simple functions func genericId(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)")
Voir les URL ci-dessous pour trouver pourquoi et comment cela fonctionne:
Comme vous le voyez, il est capable de vérifier l’identité uniquement (le deuxième test donne des résultats false
). Mais cela devrait être suffisant.
Le moyen le plus simple est de désigner le type de bloc comme @objc_block
, et maintenant vous pouvez le convertir en un AnyObject comparable à ===
. Exemple:
typealias Ftype = @objc_block (s:Ssortingng) -> () 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
J’ai cherché beaucoup. Il semble y avoir aucun moyen de comparaison de pointeur de fonction. La meilleure solution est d’encapsuler la fonction ou la fermeture dans un object hashable. Comme:
var handler:Handler = Handler(callback: { (message:Ssortingng) in //handler body }))
C’est une excellente question et bien que Chris Lattner ne veuille pas supporter cette fonctionnalité intentionnellement, comme beaucoup de développeurs, je ne peux pas non plus abandonner mes sentiments venant d’autres langages où c’est une tâche sortingviale. Il y a beaucoup d’exemples unsafeBitCast
, la plupart d’entre eux ne montrent pas l’image complète, en voici une plus détaillée :
typealias SwfBlock = () -> () typealias ObjBlock = @convention(block) () -> () func testSwfBlock(a: SwfBlock, _ b: SwfBlock) -> Ssortingng { 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) -> Ssortingng { 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?) -> Ssortingng { 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
La partie intéressante est la rapidité avec laquelle SwfBlock se lance librement dans ObjBlock, alors qu’en réalité deux blocs SwfBlock convertis seront toujours des valeurs différentes, alors que ObjBlocks ne le fera pas. Lorsque nous lançons ObjBlock à SwfBlock, la même chose leur arrive, ils deviennent deux valeurs différentes. Donc, pour préserver la référence, ce type de casting doit être évité.
Je suis toujours en train de comprendre tout ce sujet, mais une chose que je souhaitais est la possibilité d’utiliser @convention(block)
sur les méthodes class / struct, j’ai donc déposé une demande de fonctionnalité qui doit être augmentée ou expliquant pourquoi c’est une mauvaise idée. J’ai également l’impression que cette approche pourrait être mauvaise dans son ensemble, si oui, quelqu’un peut-il expliquer pourquoi?
Eh bien, ça fait 2 jours et personne n’a encore trouvé de solution, alors je vais changer mon commentaire en une réponse:
Autant que je sache, vous ne pouvez pas vérifier l’égalité ou l’identité des fonctions (comme votre exemple) et les métaclasses (par exemple, MyClass.self
):
Mais – et ce n’est qu’une idée – je ne peux pas m’empêcher de remarquer que la clause where
dans les génériques semble pouvoir vérifier l’égalité des types. Alors peut-être pouvez-vous en tirer parti, du moins pour vérifier votre identité?
Voici une solution possible (conceptuellement la même que la réponse «tuncay»). Le but est de définir une classe qui encapsule certaines fonctionnalités (par exemple, Commande):
Rapide:
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