Almacene un cierre como variable en Swift

140

En Objective-C, puede definir la entrada y salida de un bloque, almacenar uno de esos bloques que se pasa a un método, luego usar ese bloque más tarde:

// in .h

    typedef void (^APLCalibrationProgressHandler)(float percentComplete);
    typedef void (^APLCalibrationCompletionHandler)(NSInteger measuredPower, NSError *error);

    // in .m

    @property (strong) APLCalibrationProgressHandler progressHandler;
    @property (strong) APLCalibrationCompletionHandler completionHandler;

    - (id)initWithRegion:(CLBeaconRegion *)region completionHandler:(APLCalibrationCompletionHandler)handler
    {
        self = [super init];
        if(self)
        {
            ...
            _completionHandler = [handler copy];
            ..
        }

        return self;
}

- (void)performCalibrationWithProgressHandler:(APLCalibrationProgressHandler)handler
{
    ...

            self.progressHandler = [handler copy];

     ...
            dispatch_async(dispatch_get_main_queue(), ^{
                _completionHandler(0, error);
            });
     ...
}

Así que estoy tratando de hacer el equivalente en Swift:

var completionHandler:(Float)->Void={}


init() {
    locationManager = CLLocationManager()
    region = CLBeaconRegion()
    timer = NSTimer()
}

convenience init(region: CLBeaconRegion, handler:((Float)->Void)) {
    self.init()
    locationManager.delegate = self
    self.region = region
    completionHandler = handler
    rangedBeacons = NSMutableArray()
}

Al compilador no le gusta esa declaración de finalización Handler. No es que lo culpe, pero, ¿cómo defino un cierre que se puede configurar y usar más tarde en Swift?

Jay Dub
fuente
1
¿Qué error obtienes cuando compilas?
TheLazyChap

Respuestas:

335

El compilador se queja de

var completionHandler: (Float)->Void = {}

porque el lado derecho no es un cierre de la firma apropiada, es decir, un cierre que toma un argumento flotante. Lo siguiente asignaría un cierre de "no hacer nada" al controlador de finalización:

var completionHandler: (Float)->Void = {
    (arg: Float) -> Void in
}

y esto se puede acortar a

var completionHandler: (Float)->Void = { arg in }

debido a la inferencia de tipo automática.

Pero lo que probablemente desee es que el controlador de finalización se inicialice de nil la misma manera en que se inicializa una variable de instancia de Objective-C nil. En Swift esto se puede realizar con una opción :

var completionHandler: ((Float)->Void)?

Ahora la propiedad se inicializa automáticamente en nil("sin valor"). En Swift, usaría un enlace opcional para verificar si el controlador de finalización tiene un valor

if let handler = completionHandler {
    handler(result)
}

o encadenamiento opcional:

completionHandler?(result)
Martin R
fuente
1
"En Swift esto se puede realizar con una opción implícitamente sin envolver" O una opción opcional "sin envolver explícitamente" (es decir, regular)
newacct
1
¿Está usando ((Float)->Void)!algo diferente de ((Float)->Void)?? ¿No se declara una opción no inicializada con el ?valor predeterminado nilya?
Suragch 01 de
43

C objetivo

@interface PopupView : UIView
@property (nonatomic, copy) void (^onHideComplete)();
@end

@interface PopupView ()

...

- (IBAction)hideButtonDidTouch:(id sender) {
    // Do something
    ...
    // Callback
    if (onHideComplete) onHideComplete ();
}

@end

PopupView * popupView = [[PopupView alloc] init]
popupView.onHideComplete = ^() {
    ...
}

Rápido

class PopupView: UIView {
    var onHideComplete: (() -> Void)?

    @IBAction func hideButtonDidTouch(sender: AnyObject) {
        // Do something
        ....
        // Callback
        if let callback = self.onHideComplete {
            callback ()
        }
    }
}

var popupView = PopupView ()
popupView.onHideComplete = {
    () -> Void in 
    ...
}
Phước Hải Tạ
fuente
1
¿Pero la gestión de memoria se maneja automáticamente correcta? Porque en Obj-C especificas que la propiedad es "copiar", pero parece que swift no tiene esa opción y se define como "fuerte", ¿o no?
Paulius Vindzigelskis
¿Por qué es necesario copiarlo?
Dmitry
9

He proporcionado un ejemplo no estoy seguro de si esto es lo que buscas.

var completionHandler: (_ value: Float) -> ()

func printFloat(value: Float) {
    print(value)
}

completionHandler = printFloat

completionHandler(5)

Simplemente imprime 5 usando la completionHandlervariable declarada.

TheLazyChap
fuente
7

En Swift 4 y 5 . Creé una variable de cierre que contiene dos parámetros de diccionario y bool.

 var completionHandler:([String:Any], Bool)->Void = { dict, success  in
    if success {
      print(dict)
    }
  }

Llamar a la variable de cierre

self.completionHandler(["name":"Gurjinder singh"],true)
Gurjinder Singh
fuente
5

Los cierres se pueden declarar de la typealiassiguiente manera

typealias Completion = (Bool, Any, Error) -> Void

Si desea usar en su función en cualquier parte del código; puedes escribir como variable normal

func xyz(with param1: String, completion: Completion) {
}
saurabh kumar
fuente
3

Esto también funciona:

var exeBlk = {
    () -> Void in
}
exeBlk = {
    //do something
}
//instead of nil:
exeBlk = {}
marca
fuente
-1

Para mí lo siguiente estaba funcionando:

var completionHandler:((Float)->Void)!
letsdev-cwack
fuente