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?

Jay Dub
la source
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)
Martin R
la source
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 
    ...
}
Phước Hải Tạ
la source
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.

TheLazyChap
la source
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)
Gurjinder Singh
la source
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) {
}
saurabh kumar
la source
3

Cela fonctionne aussi:

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

Pour moi, suivre fonctionnait:

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