请考虑以下代码:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let validator:NSPredicate = NSPredicate(format:"SELF MATCHES %@","[A-Za-z0-9- ]+")
if(validator.evaluateWithObject(string) || string == "" /* i.e. backspace */) {
self.process(textField)
return true
}
else {
return false
}
}
我想在return语句之后实际运行self.process(textField),因为在它之前,textField中的文本尚未实际更改。这让我想知道,为什么我不能在返回语句之后执行一些代码?为什么函数总是在return语句发生时停止?
我意识到传统上返回意味着什么,但还有其他选择吗?比如,有没有办法从函数中返回一个值然后仍然继续?
一方面,这似乎是一个愚蠢的问题,但另一方面,我觉得我不能成为第一个想做这件事的人。如果我可以在运行循环的下一个循环中运行某些东西就足够了,所以也许GCD中有一些东西可以帮助我。
答案 0 :(得分:9)
自Swift 2.0起,我们有一个名为" defer"的关键字。一个关键字,允许我们指定一个代码块,我们的函数中的一个段,它将在程序控制转移到作用域之外之前执行。也许是为了清理或其他需要,即使抛出错误也需要采取行动。
延迟块内的代码执行被推迟到倒数第二个语句执行,假设最后一个是返回语句的情况。
以下是如何使用它:
func anyFunction(someParameter: Int) -> Int {
// Some code
defer {
// Code to be deferred.
}
return someValue
} // anyFunction
延迟块的位置应该放在大括号内的任何位置,总是放在return语句之前,出于逻辑原因并且还要避免警告:&# 34;返回后的代码'将永远不会被执行"。
一些例子:
答案 1 :(得分:2)
我认为您需要将流程代码移动到另一个功能。
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("view loaded")
textField.addTarget(self,
action: "textFieldDidChange:", forControlEvents: .EditingChanged)
}
func textFieldDidChange(textField: UITextField){
print("text changed: \(theTextField.text)")
self.process(textField)
}
答案 2 :(得分:1)
您的问题的答案是否定的。但解决您的问题很简单。
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let validator:NSPredicate = NSPredicate(format:"SELF MATCHES %@","[A-Za-z0-9- ]+")
if(validator.evaluateWithObject(string) || string == "" /* i.e. backspace */) {
DispatchQueue.main.async {
print("about to process textField, timestamp: \(Date().timeIntervalSince1970)")
self.process(textField)
}
print("about to return true, timestamp: \(Date().timeIntervalSince1970)")
return true
}
else {
return false
}
}
DispatchQueue.main.async
将执行推迟到运行循环的下一次迭代。使用这些 print()
语句,您将在控制台中看到这一点。您的时间戳会有所不同,但您会看到很小的差异(在本例中约为 15/1000 秒)。
即将返回true,时间戳:1612578965.103658
<块引用>即将处理textField,时间戳:1612578965.1188931
如果您需要特定的延迟,请使用 DispatchQueue.main.asyncAfter
我在这方面找到的最好的解释是 Matt Neuburg 的书“iOS 14 Programming Fundamentals with Swift”。请参阅延迟性能部分。
答案 3 :(得分:0)
在return语句之后没有语言原语可以运行任意代码。没有语言提供这个。但是,您始终可以使用闭包来嵌入和排序代码流;就像一个完成处理程序。
在某些情况下,您可能希望使用willSet
和/或didSet
。想象一下,拥有一个String属性,它是文本字段的后备存储。如果验证了字符串,则写入后备存储。然后,这将触发willSet
,可以运行您的process
代码,并根据结果直接更新textField
。
答案 4 :(得分:0)
延迟注入应该在代码的可访问语句中,否则它们将不会在块的末尾执行。基本上,它是延迟的主要思想。
答案 5 :(得分:0)
函数返回后,即该函数生命的尽头。为了保证在函数返回后首先执行某些操作,将要求调用该函数的对象在序列化环境中执行该操作。
但是,在您的用例中,该功能是由用户输入调用的,除非我们通过这种方式继承或模糊处理,否则我在您的用例中建议的是异步分配,且延迟不明显:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let validator:NSPredicate = NSPredicate(format:"SELF MATCHES %@","[A-Za-z0-9- ]+")
if(validator.evaluateWithObject(string) || string == "" /* i.e. backspace */) {
self.process(textField)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
// async allows control to fall through
// delay ensures call after return
}
return true
}
else {
return false
}
}
这是一个临界点,我个人不喜欢这种解决方案,但是替代方案非常丑陋,从用户的角度来看,它似乎是无缝的。工程学中肯定有启发式的空间,这就是这种情况。