Stocker une fermeture en tant que variable dans Swift


141

En Objective-C, vous pouvez définir l'entrée et la sortie d'un bloc, stocker un de ces blocs qui est passé à une méthode, puis utiliser ce bloc plus tard:

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

Donc j'essaye de faire l'équivilant dans 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()
}

Le compilateur n'aime pas cette déclaration de completionHandler. Non pas que je le blâme, mais comment définir une fermeture qui peut être définie et utilisée plus tard dans Swift?


1
Quelle erreur obtenez-vous lors de la compilation?
TheLazyChap

Réponses:


335

Le compilateur se plaint de

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

car le côté droit n'est pas une fermeture de la signature appropriée, c'est à dire une fermeture prenant un argument float. Ce qui suit attribuerait une fermeture "ne rien faire" au gestionnaire d'achèvement:

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

et cela peut être raccourci à

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

en raison de l'inférence de type automatique.

Mais ce que vous voulez probablement, c'est que le gestionnaire de complétion soit initialisé de nil la même manière qu'une variable d'instance Objective-C est initialisée nil. Dans Swift, cela peut être réalisé avec une option :

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

Maintenant, la propriété est automatiquement initialisée à nil("aucune valeur"). Dans Swift, vous utiliseriez une liaison facultative pour vérifier si le gestionnaire d'achèvement a une valeur

if let handler = completionHandler {
    handler(result)
}

ou chaînage optionnel:

completionHandler?(result)

1
"Dans Swift, cela peut être réalisé avec un optionnel implicitement déroulé" Ou un "explicitement déballé" (c'est-à-dire régulier) optionnel
newacct

1
Est-ce que l'utilisation est ((Float)->Void)!différente de ((Float)->Void)?? Ne déclare pas un optionnel non initialisé avec la ?valeur par défaut nildéjà?
Suragch

43

Objectif 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 = ^() {
    ...
}

Rapide

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 
    ...
}

1
Mais la gestion de la mémoire est-elle automatiquement gérée correctement? Parce que dans Obj-C, vous spécifiez que cette propriété est "copie", mais swift semble ne pas avoir cette option et est définie comme "forte" à la place, ou le fait-elle?
Paulius Vindzigelskis

Pourquoi est-il nécessaire de le copier?
Dmitry le

9

J'ai donné un exemple, je ne sais pas si c'est ce que vous recherchez.

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

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

completionHandler = printFloat

completionHandler(5)

Il imprime simplement 5 en utilisant la completionHandlervariable déclarée.


7

Dans Swift 4 et 5 . J'ai créé une variable de fermeture contenant deux dictionnaire de paramètres et un booléen.

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

Appel de la variable de fermeture

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

5

Les fermetures peuvent être déclarées typealiascomme ci-dessous

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

Si vous souhaitez utiliser dans votre fonction n'importe où dans le code; vous pouvez écrire comme une variable normale

func xyz(with param1: String, completion: Completion) {
}

3

Cela fonctionne aussi:

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.