如何在Swift中的函数中的return语句之后运行代码?

时间:2016-03-20 17:33:25

标签: swift swift2 return

请考虑以下代码:

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中有一些东西可以帮助我。

6 个答案:

答案 0 :(得分:9)

自Swift 2.0起,我们有一个名为" defer"的关键字。一个关键字,允许我们指定一个代码块,我们的函数中的一个段,它将在程序控制转移到作用域之外之前执行。也许是为了清理或其他需要,即使抛出错误也需要采取行动。

延迟块内的代码执行被推迟到倒数第二个语句执行,假设最后一个是返回语句的情况。

以下是如何使用它:

func anyFunction(someParameter: Int) -> Int {

    // Some code

    defer {

        // Code to be deferred.

    }

    return someValue

} // anyFunction

延迟块的位置应该放在大括号内的任何位置,总是放在return语句之前,出于逻辑原因并且还要避免警告:&# 34;返回后的代码'将永远不会被执行"。

一些例子:

enter image description here

enter image description here

答案 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
    }
}

这是一个临界点,我个人不喜欢这种解决方案,但是替代方案非常丑陋,从用户的角度来看,它似乎是无缝的。工程学中肯定有启发式的空间,这就是这种情况。