Armazene um fechamento como uma variável no Swift

140

No Objective-C, você pode definir a entrada e a saída de um bloco, armazenar um desses blocos que são passados ​​para um método e usá-lo posteriormente:

// 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);
            });
     ...
}

Então, eu estou tentando fazer o equivalente em 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()
}

O compilador não gosta dessa declaração de conclusãoHandler. Não que eu seja o culpado, mas como definir um fechamento que pode ser definido e usado posteriormente no Swift?

Jay Dub
fonte
1
Que erro você está recebendo quando compila?
TheLazyChap

Respostas:

334

O compilador reclama

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

porque o lado direito não é um fechamento da assinatura apropriada, ou seja, um fechamento com argumento de flutuação. O seguinte atribuiria um encerramento "não fazer nada" ao manipulador de conclusão:

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

e isso pode ser reduzido para

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

devido à inferência automática de tipo.

Mas o que você provavelmente deseja é que o manipulador de conclusão seja inicializado nil da mesma maneira que uma variável de instância Objective-C é inicializada nil. No Swift, isso pode ser realizado com um opcional :

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

Agora a propriedade é inicializada automaticamente para nil("sem valor"). No Swift, você usaria a ligação opcional para verificar se o manipulador de conclusão possui um valor

if let handler = completionHandler {
    handler(result)
}

ou encadeamento opcional:

completionHandler?(result)
Martin R
fonte
1
"No Swift, isso pode ser realizado com um opcional implicitamente desembrulhado" ou um opcional "explicitamente desembrulhado" (regular)
newacct
1
O uso é ((Float)->Void)!diferente de ((Float)->Void)?? A declaração de um opcional não inicializado com o ?padrão niljá é?
Suragch 01/07/16
43

Objetivo-C

@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ạ
fonte
1
Mas o gerenciamento de memória é tratado automaticamente correto? Porque no Obj-C Você especifica essa propriedade como "cópia", mas o Swift parece não ter essa opção e é definido como "forte", ou não?
Paulius Vindzigelskis
Por que é necessário copiá-lo?
Dmitry
9

Forneci um exemplo, não tenho certeza se é isso que você procura.

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

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

completionHandler = printFloat

completionHandler(5)

Simplesmente imprime 5 usando a completionHandlervariável declarada.

TheLazyChap
fonte
7

Nos Swift 4 e 5 . Eu criei uma variável de fechamento contendo dois dicionário de parâmetros e bool.

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

Chamando a variável de fechamento

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

Os fechamentos podem ser declarados typealiascomo abaixo

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

Se você deseja usar em sua função em qualquer lugar no código; você pode escrever como variável normal

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

Isso também funciona:

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

Para mim, o seguinte estava funcionando:

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