Search code examples
swiftclosuresprotocols

I want to define closure variables and use them later, How do I do?


Below, there are one protocol which has two closure variables and a functions. And there is also a class conforming to the protocol.

import Foundation

protocol TestProtocol: class {
    var firstClosure: () -> () { get set }
    var secondClosure: () -> () { get set }
    func testFunction()
}

extension TestProtocol {
    func testFunction() {
        _ = firstClosure
        _ = secondClosure
    }
}

class A: TestProtocol {
    var firstClosure: () -> ()
    var secondClosure: () -> ()

    var number = 0

    init() {
        print(number)
        firstClosure = {
            self.number = 1
        }()
        print(number)
        secondClosure = {
            self.number = 2
        }()
        print(number)
        testFunction()
        print(number)
    }
}

let a = A()

My expected result is 0, 0, 0, 2. But, when I define closure variable, the closure variable execute immediately. What should I do?


Solution

  • The issue is that you aren't defining closures, you're defining Voids due to the fact that you execute the closures right after defining them by writing () after the closure. The type of firstClosure and secondClosure are both Void, which is a typealias for (). If you wanted to declare a closure that takes no input arguments and returns no value (or rather returns Void), you need to declare it as () -> ().

    protocol TestProtocol: class {
        var firstClosure: () -> () { get set }
        var secondClosure: () -> () { get set }
        func testFunction()
    }
    
    extension TestProtocol {
        func testFunction() {
            firstClosure()
            secondClosure()
        }
    }
    

    You should also modify your conforming class to directly set the closure's instead of setting them in the initializer and not to call a function with side effects (testFunction) in the initializer, but rather call it when you actually need it after initialisation. You need to declare your closures as lazy, since they access self.

    class A: TestProtocol {
        var number = 0
        lazy var firstClosure: () -> Void = {
            self.number = 1
        }
        lazy var secondClosure: () -> Void = {
            self.number = 2
        }
    }
    
    A().testFunction()