设置两个变量后如何调用方法

How to call a method once two variables have been set

我正在使用 iOS Swift,并且我试图了解如何在设置了两个变量的值(非空值)后执行 method请求完成后。

在阅读了一些文档之后,我发现了一些有趣的概念。第一个是 didSet,作为 observer.

如果我只需要一个变量

,我可以通过简单地使用 didSet 来调用使用此方法的方法

didSet

var myVar: String  0 {
    didSet {
        print("Hello World.")

    }
}

不过,我还要等第二个myVar2,所以不行。

我还发现了 DispatchQueue,我可以用它在调用方法之前等待一秒钟(我使用的请求非常快)

调度队列

 DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute:  {
            print("Hello world")
        })

但我认为这个解决方案效率不高。

是否可以将这两个变量或请求结合起来,以便在完成设置值后调用方法?

更新 我试图复制 David 的答案,我认为这是正确的,但我在每个 \.

上得到以下错误

Type of expression is ambiguous without more context

我在这里复制我当前的代码

var propertiesSet: [KeyPath<SearchViewController, Car>:Bool] = [\SearchViewController.firstCar:false, \SearchViewController.secondCar:false] {
    didSet {
        if propertiesSet.allSatisfy({ [=12=].value }) {
            // Conditions passed, execute your custom logic
            print("All Set")
        } else {
            print("Not yet")
        }
    }
}

 var firstCar: Car? {
     didSet {
        propertiesSet[\SearchViewController.firstCar] = true
     }
 }

 var secondCar: Car?  {
     didSet {
        propertiesSet[\SearchViewController.secondCar] = true
     }
 }

变量是单独设置的,每个变量都有自己的要求。

您可以创建您的属性 optional 并在调用您的 function.

之前检查它们是否都设置了值
var varA: String? = nil {
    didSet {
        if varA != nil && varB != nil {
            myFunc()
        }
    }
}

var varB: String? = nil {
    didSet {
        if varA != nil && varB != nil {
            myFunc()
        }
    }
}

或者您可以在每个 didSet 上调用 function 并在 function 的开头使用 guard 条件来检查您的两个属性是否具有值,或摆脱困境:

var varA: String? = nil {
    didSet {
        myFunc()
    }
}

var varB: String? = nil {
    didSet {
        myFunc()
    }
}

func myFunc() {
    guard varA != nil && varB != nil else { return }
    // your code
}

您需要为所有需要设置的变量添加一个 didSet 才能使您的条件通过。同时创建一个 Dictionary 包含 KeyPath 到您需要设置的变量和一个 Bool 表示它们是否已经设置。

然后您可以在 Dictionary 上创建一个 didSet,其中包含所需变量的 "set-state",当它们的所有值都为 true 时,这意味着它们全部已设置,执行您的代码。

由于使用 Dictionary 而不是像 if aSet && bSet && cSet 这样很容易失控的手动编写条件,此解决方案可以很好地扩展到任意数量的属性。

class AllSet {
    var propertiesSet: [KeyPath<AllSet, String>:Bool] = [\.myVar:false, \.myVar2:false] {
        didSet {
            if propertiesSet.allSatisfy({ [=10=].value }) {
                // Conditions passed, execute your custom logic
                print("All Set")
            } else {
                print("Not yet")
            }
        }
    }

    var myVar: String {
        didSet {
            propertiesSet[\.myVar] = true
        }
    }

    var myVar2: String {
        didSet {
            propertiesSet[\.myVar2] = true
        }
    }

    init(myVar: String, myVar2: String) {
        self.myVar = myVar
        self.myVar2 = myVar2
    }
}

let all = AllSet(myVar: "1", myVar2: "2")
all.myVar = "2" // prints "Not yet"
all.myVar2 = "1" // prints "All set"

首先,您应该非常仔细地考虑您的语义是什么。当你说 "set," 时,你的意思是 "assigned a value" 还是 "assigned a non-nil value?"(在这种情况下我假设你指的是后者。)你应该问问自己,如果你的方法已经解雇,然后设置另一个值?如果其中一个属性设置了一个值,然后设置了 nil,然后设置了另一个值怎么办?应该触发该方法 1 次、2 次还是 3 次?

只要有可能,您就应该努力通过要求将值设置在一起(例如,在 init 而不是可变属性中)来避免此类问题。

但显然在某些情况下这是必要的(UI 是最常见的)。

如果您的目标是 iOS 13 岁以上,您应该探索 Combine 以解决此类问题。作为一种方法:

class Model: ObservableObject {

    @Published var first: String?
    @Published var second: String?
    @Published var ready = false

    private var observers: Set<AnyCancellable> = []

    init() {
        $first.combineLatest($second)
            .map { [=10=] != nil &&  != nil }
            .assign(to: \.ready, on: self)
            .store(in: &observers)
    }
}

let model = Model()

var observers: Set<AnyCancellable> = []

model.$ready
    .sink { if [=10=] { print("GO!") } }
    .store(in: &observers)

model.first = "ready"
model.second = "set"
// prints "GO!"

另一种方法是将包含可选值的附带状态与您正在构造的实际对象分开,而后者不包含。

// Possible parameters for Thing
struct Parameters {
    var first: String?
    var second: String?
}

// The thing you're actually constructing that requires all the parameters
struct Thing {
    let first: String
    let second: String

    init?(parameters: Parameters) {
        guard let first = parameters.first,
            let second = parameters.second
            else { return nil }
        self.first = first
        self.second = second
    }
}

class TheUIElement {
    // Any time the parameters change, try to make a Thing
    var parameters: Parameters = Parameters() {
        didSet {
            thing = Thing(parameters: parameters)
        }
    }

    // If you can make a Thing, then Go!
    var thing: Thing? {
        didSet {
            if thing != nil { print("GO!") }
        }
    }
}

let element = TheUIElement()
element.parameters.first = "x"
element.parameters.second = "y"
// Prints "GO!"