Przechowuj zamknięcie jako zmienną w Swift

140

W Objective-C możesz zdefiniować wejście i wyjście bloku, zapisać jeden z tych bloków, który jest przekazany do metody, a następnie użyć tego bloku później:

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

Więc próbuję zrobić to samo w 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()
}

Kompilatorowi nie podoba się ta deklaracja CompleteHandler. Nie to, żebym go winił, ale jak zdefiniować zamknięcie, które można ustawić i użyć później w Swift?

Jay Dub
źródło
1
Jaki błąd otrzymujesz podczas kompilacji?
TheLazyChap

Odpowiedzi:

335

Kompilator narzeka na

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

ponieważ prawa strona nie jest zamknięciem odpowiedniego podpisu, czyli domknięciem przyjmującym argument typu float. Poniższe polecenie przypisałoby zamknięcie „nic nie rób” do procedury obsługi zakończenia:

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

i można to skrócić do

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

ze względu na automatyczne wnioskowanie o typie.

Ale prawdopodobnie chcesz, aby procedura obsługi zakończenia została zainicjowana nil w ten sam sposób, w jaki jest zainicjowana zmienna instancji Objective-C nil. W Swift można to zrealizować za pomocą opcjonalnego :

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

Teraz właściwość jest automatycznie inicjalizowana na nil(„brak wartości”). W Swift możesz użyć opcjonalnego powiązania, aby sprawdzić, czy program obsługi zakończenia ma wartość

if let handler = completionHandler {
    handler(result)
}

lub opcjonalnie łańcuch:

completionHandler?(result)
Martin R.
źródło
1
"W Swift można to zrealizować za pomocą niejawnie rozpakowanego opcjonalnego" Lub "jawnie rozpakowanego" (tj.
Zwykłego
1
Czy używanie ((Float)->Void)!jest inne niż ((Float)->Void)?? Czy nie jest już deklarowanie niezainicjowanego opcjonalnego z ?wartością domyślną nil?
Suragch
43

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

Szybki

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ạ
źródło
1
Ale czy zarządzanie pamięcią jest obsługiwane automatycznie? Ponieważ w Obj-C określasz tę właściwość jako "kopiuj", ale wydaje się, że swift nie ma tej opcji i zamiast tego jest zdefiniowany jako "silny", czy tak jest?
Paulius Vindzigelskis
Dlaczego trzeba to skopiować?
Dmitry
9

Podałem przykład, nie jestem pewien, czy o to ci chodzi.

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

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

completionHandler = printFloat

completionHandler(5)

Po prostu wypisuje 5 używając completionHandlerzadeklarowanej zmiennej.

TheLazyChap
źródło
7

W Swift 4 i 5 . Utworzyłem zmienną zamykającą zawierającą słownik dwóch parametrów i bool.

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

Wywołanie zmiennej zamknięcia

self.completionHandler(["name":"Gurjinder singh"],true)
Gurjinder Singh
źródło
5

Zamknięcia można zadeklarować typealiasjak poniżej

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

Jeśli chcesz użyć swojej funkcji w dowolnym miejscu kodu; możesz pisać jak normalne zmienne

func xyz(with param1: String, completion: Completion) {
}
saurabh kumar
źródło
3

To też działa:

var exeBlk = {
    () -> Void in
}
exeBlk = {
    //do something
}
//instead of nil:
exeBlk = {}
znak
źródło
-1

U mnie działało:

var completionHandler:((Float)->Void)!
letdev-cwack
źródło