2

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

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

didSet如果我只需要一个变量,我可以使用此方法调用该方法

设置

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

    }
}

不过,我还需要等待第二个myVar2,所以它不起作用。

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

调度队列

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

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

无论如何将这两个变量或请求组合起来,以便在完成设置值后调用方法?

更新 我试图复制大卫的答案,我认为这是正确的,但我得到以下错误\.

在没有更多上下文的情况下,表达式的类型是模棱两可的

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

var propertiesSet: [KeyPath<SearchViewController, Car>:Bool] = [\SearchViewController.firstCar:false, \SearchViewController.secondCar:false] {
    didSet {
        if propertiesSet.allSatisfy({ $0.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
     }
 }

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

4

3 回答 3

2

您可以optional在调用您的function.

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

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

或者你可以调用你function的每一个,并在你的开头didSet使用一个条件来检查你的两个属性是否都有值,或者退出:guardfunction

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

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

func myFunc() {
    guard varA != nil && varB != nil else { return }
    // your code
}
于 2020-05-20T13:21:11.607 回答
2

首先,您应该非常仔细地考虑您的语义在这里。当您说“设置”时,您的意思是“分配了一个值”还是“分配了一个非零值”?(在这种情况下,我假设您的意思是后者。)您应该问自己,如果您的方法已经触发,然后设置了另一个值,会发生什么?如果其中一个属性设置了一个值,然后设置了 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 { $0 != nil && $1 != nil }
            .assign(to: \.ready, on: self)
            .store(in: &observers)
    }
}

let model = Model()

var observers: Set<AnyCancellable> = []

model.$ready
    .sink { if $0 { 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!"
于 2020-05-20T14:06:08.050 回答
1

您需要将 a 添加didSet到需要为您的条件通过设置的所有变量中。还为需要设置的变量创建一个Dictionary包含s 和一个表示它们是否已经设置的变量。KeyPathBool

然后,您可以didSetDictionary包含所需变量的“设置状态”的地方创建一个,当它们的所有值都true意味着它们都已设置时,执行您的代码。

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

class AllSet {
    var propertiesSet: [KeyPath<AllSet, String>:Bool] = [\.myVar:false, \.myVar2:false] {
        didSet {
            if propertiesSet.allSatisfy({ $0.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"
于 2020-05-20T13:21:32.243 回答