swift - metáfora - metafora juridica ejemplos
Cómo usar el método de instancia como devolución de llamada para la función que solo toma func o cierre literal (2)
En mi caso, la función que quería llamar desde mi cierre estaba en AppDelegate. Así que pude usar un delegado para llamar a la función desde el cierre sin usar self. Si es una buena idea o no, es algo que alguien con más experiencia tendrá que comentar.
self.pingSocket = CFSocketCreate(kCFAllocatorDefault, AF_INET, SOCK_DGRAM, IPPROTO_ICMP,CFSocketCallBackType.dataCallBack.rawValue, {socket, type, address, data, info in
//type is CFSocketCallBackType
guard let socket = socket, let address = address, let data = data, let info = info else { return }
// stuff deleted, including use of C pointers
let appDelegate = NSApplication.shared.delegate as! AppDelegate
appDelegate.receivedPing(ip: sourceIP, sequence: sequence, id: id)
//}
return
}, &context)
extension AppDelegate: ReceivedPingDelegate {
func receivedPing(ip: UInt32, sequence: UInt16, id: UInt16) {
// stuff deleted
}
}
protocol ReceivedPingDelegate: class {
func receivedPing(ip: UInt32, sequence: UInt16, id: UInt16)
}
En "ViewController.swift" estoy creando esta devolución de llamada:
func callback(cf:CFNotificationCenter!,
ump:UnsafeMutablePointer<Void>,
cfs:CFString!,
up:UnsafePointer<Void>,
cfd:CFDictionary!) -> Void {
}
Usando este observador:
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
nil,
self.callback,
"myMESSage",
nil,
CFNotificationSuspensionBehavior.DeliverImmediately)
Resultados en este error del compilador:
"El puntero de función de CA solo puede formarse a partir de una referencia a un ''func'' o un cierre literal"
La devolución de llamada es un puntero a una función C, y en Swift solo puede pasar una función global o un cierre (que no captura ningún estado), pero no un método de instancia.
Entonces esto funciona:
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
nil,
{ (_, observer, name, _, _) in
print("received notification: /(name)")
},
"myMessage",
nil,
.DeliverImmediately)
Pero dado que el cierre no puede capturar el contexto, no tiene referencia directa a
self
y sus propiedades y métodos de instancia.
Por ejemplo, no puedes agregar
self.label.stringValue = "got it"
// error: a C function pointer cannot be formed from a closure that captures context
dentro del cierre para actualizar la interfaz de usuario cuando llega una notificación.
Hay una solución, pero es un poco complicada debido al estricto sistema de tipos de Swift.
De manera similar como en
Swift 2 - UnsafeMutablePointer <Void> a object
, puede convertir el puntero en
self
en un puntero vacío, pasarlo como parámetro de
observer
al registro y convertirlo nuevamente en un puntero de objeto en la devolución de llamada.
class YourClass {
func callback(name : String) {
print("received notification: /(name)")
}
func registerObserver() {
// Void pointer to `self`:
let observer = UnsafePointer<Void>(Unmanaged.passUnretained(self).toOpaque())
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
observer,
{ (_, observer, name, _, _) -> Void in
// Extract pointer to `self` from void pointer:
let mySelf = Unmanaged<YourClass>.fromOpaque(
COpaquePointer(observer)).takeUnretainedValue()
// Call instance method:
mySelf.callback(name as String)
},
"myMessage",
nil,
.DeliverImmediately)
}
// ...
}
El cierre actúa como un "trampolín" para el método de instancia.
El puntero es una referencia no retenida, por lo tanto, debe asegurarse de que el observador se elimine antes de que se desasigne el objeto.
Actualización para Swift 3:
class YourClass {
func callback(_ name : String) {
print("received notification: /(name)")
}
func registerObserver() {
// Void pointer to `self`:
let observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
observer,
{ (_, observer, name, _, _) -> Void in
if let observer = observer, let name = name {
// Extract pointer to `self` from void pointer:
let mySelf = Unmanaged<YourClass>.fromOpaque(observer).takeUnretainedValue()
// Call instance method:
mySelf.callback(name.rawValue as String)
}
},
"myMessage" as CFString,
nil,
.deliverImmediately)
}
// ...
}
Consulte también Cómo lanzarse al tipo UnsafeMutablePointer <Void> en swift para obtener más información sobre el "puente" entre los punteros de objeto y los punteros de C.