这本书说“函数和闭包都是引用类型”,那么如何判断它们的引用是否相等呢? == 和 === 并不起作用。
func a() { }
let å = a
let b = å === å // Could not find an overload for === that accepts the supplied arguments
这本书说“函数和闭包都是引用类型”,那么如何判断它们的引用是否相等呢? == 和 === 并不起作用。
func a() { }
let å = a
let b = å === å // Could not find an overload for === that accepts the supplied arguments
Hashable
的值类型(它可以实现 callAsFunction()
)具有与闭包相同信息的方式可以工作呢?采用这种方法,甚至可以从数组中删除实例而不必将它们存储在另一个位置并重新创建它们。 - Feuermurmel我进行了大量搜索,似乎没有办法比较函数指针。我得到的最佳解决方案是将函数或闭包封装在可哈希的对象中,例如:
var handler:Handler = Handler(callback: { (message:String) in
//handler body
}))
最简单的方法是将块类型指定为@objc_block
,然后您可以将其强制转换为与===
可比较的任何对象。示例:
最简单的方式是将块类型指定为@objc_block
,现在您可以将其转换为可与===
进行比较的任何对象。例如:
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
2021年更新: 将@objc_block
更改为@convention(block)
以支持Swift 2.x
及之后的版本(这些版本不识别@objc_block
)。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
}
以下是演示:
// 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)")
false
)。但这应该已经足够了。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
unsafeBitCast
的例子,但大多数并没有展示全貌,这里有一个更详细的例子: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
@convention(block)
,所以我提出了一个功能请求,需要赞成或解释为什么这是一个坏主意。我也感到这种方法可能完全不好,如果是这样,有人可以解释一下原因吗?Struct S { func f(_: Int) -> Bool }
时,实际上你拥有一个类型为 (S) -> (Int) -> Bool
的函数 S.f
。这个函数是可以共享的,它仅由其显式参数进行参数化。但是,当您将其用作实例方法(通过在对象上调用该方法隐式绑定 self
参数,例如 S().f
,或者通过显式绑定它,例如 S.f(S())
)时,您将创建一个新的闭包对象。该对象存储指向 S.f
(可以共享)的指针,但也存储了您的实例(self
,即 S()
)。 - AlexanderS
实例中是唯一的。如果闭包指针相等是可能的,那么你会惊讶地发现 s1.f
不是与 s2.f
相同的指针(因为一个是引用 s1
和 f
的闭包对象,而另一个是引用 s2
和 f
的闭包对象)。 - Alexander这不是通用解决方案,但如果要实现监听器模式,我最终会在注册期间返回函数的“id”,以便稍后用它注销(这是一种对原始问题“listeners”情况的变通方法,因为通常来说注销最终会涉及检查函数是否相等,这至少不像其他答案所说的那样“简单”)。
大致如下:
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);
}
}
key
,并在注销时传递它即可。private var listenerId = 0
,并在重新注册监听器时递增它并返回,以避免使用复杂的 UUID().uuidString
。 - mikep两天过去了,没有人提供解决方案,所以我将我的评论改为答案:
据我所知,您无法检查函数(例如您的示例)和元类(例如 MyClass.self
)的相等性或标识:
但是 - 这只是一个想法 - 我不禁注意到泛型中的where
从句似乎能够检查类型的相等性。因此,您可能可以利用它,至少用于检查标识?
你可以使用callAsFunction
方法,例如:
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
class Function<Type>: NSObject {
let value: (Type) -> Void
init(_ function: @escaping (Type) -> Void) {
value = function
}
}
MyClass.self
)的相等性。 - Jiaaroå
上使用变音符号来引用a
的用法真的很有趣。你在这里探索的是一种约定吗?(我不知道我是否真的喜欢它;但它似乎非常强大,特别是在纯函数式编程中。) - Rob Napier