Buku tersebut mengatakan bahwa "fungsi dan penutup adalah tipe referensi". Jadi, bagaimana Anda mengetahui apakah referensinya sama? == dan === tidak berfungsi.
func a() { }
let å = a
let b = å === å // Could not find an overload for === that accepts the supplied arguments
MyClass.self
)å
untuk referensia
sangat menarik. Apakah ada konvensi yang Anda jelajahi di sini? (Saya tidak tahu apakah saya benar-benar suka atau tidak; tetapi sepertinya itu bisa sangat kuat, terutama dalam pemrograman fungsional murni.)Jawaban:
Chris Lattner menulis di forum pengembang:
https://devforums.apple.com/message/1035180#1035180
Ini berarti bahwa Anda tidak boleh mencoba membandingkan penutupan untuk kesetaraan karena pengoptimalan dapat memengaruhi hasil.
sumber
Saya banyak mencari. Tampaknya tidak ada cara untuk membandingkan penunjuk fungsi. Solusi terbaik yang saya dapatkan adalah merangkum fungsi atau penutupan dalam objek hashable. Suka:
var handler:Handler = Handler(callback: { (message:String) in //handler body }))
sumber
Cara termudah adalah menetapkan tipe blok sebagai
@objc_block
, dan sekarang Anda dapat mentransmisikannya ke AnyObject yang sebanding dengan===
. Contoh: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
sumber
Saya sudah mencari jawabannya juga. Dan akhirnya saya menemukannya.
Yang Anda butuhkan adalah penunjuk fungsi aktual dan konteksnya yang tersembunyi di objek fungsi.
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 }
Dan inilah demonya:
// 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)")
Lihat URL di bawah untuk mengetahui mengapa dan bagaimana cara kerjanya:
Seperti yang Anda lihat, ini hanya mampu memeriksa identitas (hasil tes ke-2
false
). Tapi itu seharusnya sudah cukup.sumber
Ini adalah pertanyaan yang bagus dan sementara Chris Lattner dengan sengaja tidak ingin mendukung fitur ini, saya, seperti banyak pengembang, juga tidak dapat melepaskan perasaan saya yang berasal dari bahasa lain di mana ini adalah tugas yang sepele. Ada banyak
unsafeBitCast
contoh, kebanyakan tidak menunjukkan gambaran lengkap, berikut ini lebih detailnya :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
Bagian yang menarik adalah betapa cepatnya melemparkan SwfBlock ke ObjBlock, namun pada kenyataannya dua blok SwfBlock yang dicor akan selalu memiliki nilai yang berbeda, sementara ObjBlocks tidak. Saat kami mentransmisikan ObjBlock ke SwfBlock, hal yang sama terjadi pada mereka, keduanya menjadi dua nilai yang berbeda. Jadi, untuk mempertahankan referensi, pengecoran semacam ini harus dihindari.
Saya masih memahami keseluruhan subjek ini, tetapi satu hal yang saya inginkan adalah kemampuan untuk menggunakan
@convention(block)
metode kelas / struct, jadi saya mengajukan permintaan fitur yang memerlukan pemungutan suara atau menjelaskan mengapa itu ide yang buruk. Saya juga merasa pendekatan ini mungkin buruk secara keseluruhan, jika demikian, adakah yang bisa menjelaskan mengapa?sumber
Struct S { func f(_: Int) -> Bool }
, Anda sebenarnya memiliki fungsi tipeS.f
yang memiliki tipe(S) -> (Int) -> Bool
. Fungsi ini dapat dibagikan. Ini hanya diparameterisasi oleh parameter eksplisitnya. Saat Anda menggunakannya sebagai metode instance (baik dengan mengikatself
parameter secara implisit dengan memanggil metode pada objek, misalnyaS().f
, atau dengan mengikatnya secara eksplisit, misalnyaS.f(S())
), Anda membuat objek penutupan baru. Objek ini menyimpan pointer keS.f
(yang bisa dibagi), but also to your instance (
self, the
S () `).S
. Jika persamaan closure pointer dimungkinkan, maka Anda akan terkejut menemukan bahwas1.f
itu bukan pointer yang samas2.f
(karena satu adalah objek closure yang mereferensikans1
danf
, dan yang lainnya adalah objek closure yang mereferensikans2
danf
).Berikut adalah satu solusi yang mungkin (secara konseptual sama dengan jawaban 'tuncay'). Intinya adalah untuk menentukan kelas yang membungkus beberapa fungsionalitas (misalnya, Perintah):
Cepat:
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
Jawa:
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
sumber
Nah sudah 2 hari dan tidak ada yang ikut campur dengan solusi, jadi saya akan mengubah komentar saya menjadi jawaban:
Sejauh yang saya tahu, Anda tidak dapat memeriksa persamaan atau identitas fungsi (seperti contoh Anda) dan metaclasses (misalnya,
MyClass.self
):Tetapi - dan ini hanya sebuah ide - saya tidak bisa tidak memperhatikan bahwa
where
klausa dalam obat generik tampaknya dapat memeriksa persamaan jenis. Jadi mungkin Anda bisa memanfaatkannya, setidaknya untuk memeriksa identitas?sumber
Bukan solusi umum, tetapi jika seseorang mencoba menerapkan pola pendengar, saya akhirnya mengembalikan "id" dari fungsi tersebut selama pendaftaran sehingga saya dapat menggunakannya untuk membatalkan pendaftaran nanti (yang merupakan semacam solusi untuk pertanyaan asli untuk kasus "pendengar" karena biasanya unregistering dilakukan pemeriksaan fungsi untuk persamaan, yang setidaknya tidak "sepele" seperti jawaban lainnya).
Jadi seperti ini:
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); } }
Sekarang Anda hanya perlu menyimpan yang
key
dikembalikan oleh fungsi "register" dan meneruskannya saat membatalkan pendaftaran.sumber
Solusi saya adalah membungkus fungsi ke kelas yang memperluas NSObject
class Function<Type>: NSObject { let value: (Type) -> Void init(_ function: @escaping (Type) -> Void) { value = function } }
sumber
Saya tahu saya menjawab pertanyaan ini enam tahun terlambat, tetapi saya pikir ada baiknya melihat motivasi di balik pertanyaan itu. Penanya berkomentar:
Jadi saya kira si penanya ingin mempertahankan daftar panggilan balik, seperti ini:
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 }) } }
Tapi kita tidak bisa menulis
removeCallback
seperti itu, karena==
tidak berfungsi untuk fungsi. (Tidak juga===
.)Berikut cara lain untuk mengelola daftar panggilan balik Anda. Kembalikan objek pendaftaran dari
addCallback
, dan gunakan objek pendaftaran untuk menghapus callback. Di sini di tahun 2020, kita bisa menggunakan CombineAnyCancellable
sebagai registrasi.API yang direvisi terlihat seperti ini:
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) } } }
Sekarang, saat Anda menambahkan callback, Anda tidak perlu menyimpannya untuk diteruskan
removeCallback
nanti. Tidak adaremoveCallback
metode. Sebagai gantinya, Anda menyimpanAnyCancellable
, dan memanggilcancel
metodenya untuk menghapus callback. Lebih baik lagi, jika Anda menyimpan propertiAnyCancellable
in an instance, maka itu akan membatalkan dirinya sendiri secara otomatis saat instance dimusnahkan.sumber