Сохраните закрытие как переменную в Swift

144

В Objective-C вы можете определить вход и выход блока, сохранить один из тех блоков, которые передаются в метод, а затем использовать этот блок позже:

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

Итак, я пытаюсь сделать эквивалент в 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()
}

Компилятору не нравится это объявление завершенияHandler. Не то чтобы я винил это, но как мне определить закрытие, которое можно установить и использовать позже в Swift?

Джей Даб
источник
1
Какую ошибку вы получаете при компиляции?
TheLazyChap 07

Ответы:

340

Компилятор жалуется на

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

потому что правая часть не является закрытием соответствующей сигнатуры, то есть закрытием, принимающим аргумент с плавающей запятой. Следующее будет назначать закрытие "ничего не делать" обработчику завершения:

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

и это можно сократить до

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

из-за автоматического вывода типа.

Но вы, вероятно, хотите, чтобы обработчик завершения инициализировался так nil же, как инициализируется переменная экземпляра Objective-C nil. В Swift это можно реализовать с помощью необязательного :

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

Теперь свойство автоматически инициализируется nil(«без значения»). В Swift вы можете использовать необязательную привязку для проверки того, что обработчик завершения имеет значение

if let handler = completionHandler {
    handler(result)
}

или необязательная цепочка:

completionHandler?(result)
Мартин Р
источник
1
«В Swift это может быть реализовано с помощью неявно развернутой необязательной опции» Или «явно развернутой» (то есть обычной) необязательной опции
newacct 05
1
Использует что- ((Float)->Void)!то другое, чем ((Float)->Void)?? Не объявляет ли еще неинициализированный необязательный ?параметр по умолчанию nil?
Suragch 01
43

Цель-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 = ^() {
    ...
}

Swift

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
источник
1
Но правильно ли выполняется управление памятью? Потому что в Obj-C вы указываете, что это свойство должно быть «копией», но у swift, похоже, нет этой опции, и вместо этого он определяется как «сильный», или это так?
Паулюс Виндзигельскис
Зачем нужно его копировать?
Дмитрий
10

Я привел пример, не уверен, что это то, что вам нужно.

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

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

completionHandler = printFloat

completionHandler(5)

Он просто печатает 5, используя completionHandlerобъявленную переменную.

TheLazyChap
источник
7

В Swift 4 и 5 . Я создал закрывающую переменную, содержащую словарь с двумя параметрами и bool.

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

Вызов закрывающей переменной

self.completionHandler(["name":"Gurjinder singh"],true)
Гурджиндер Сингх
источник
5

Затворы могут быть объявлены , typealiasкак показано ниже

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

Если вы хотите использовать в своей функции в любом месте кода; вы можете писать как обычную переменную

func xyz(with param1: String, completion: Completion) {
}
Саураб Кумар
источник
4

Это тоже работает:

var exeBlk = {
    () -> Void in
}
exeBlk = {
    //do something
}
//instead of nil:
exeBlk = {}
отметка
источник
-1

Для меня работало следующее:

var completionHandler:((Float)->Void)!
Letdev-cwack
источник