繁体   English   中英

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

[英]How can I run code after the return statement in a function in Swift?

考虑以下代码:

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 语句之后执行一些代码? 为什么函数总是在 return 语句发生时停止?

我意识到传统上这就是 return 的意思,但还有其他选择吗? 比如,有没有办法从函数返回一个值然后继续运行?

一方面,这似乎是一个愚蠢的问题,但另一方面,我觉得我不可能是第一个想要这样做的人。 如果我可以在运行循环的下一个循环中运行一些东西就足够了,所以也许 GCD 中有一些东西可以帮助我。

从 Swift 2.0 开始,我们有了名为“defer”的关键字。 一个关键字,它允许我们指定一个代码块,我们函数内的一个段,将在程序控制转移到范围之外之前执行。 也许是为了清理或其他需要,即使抛出错误也需要发生的操作。

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

使用方法如下:

func anyFunction(someParameter: Int) -> Int {

    // Some code

    defer {

        // Code to be deferred.

    }

    return someValue

} // anyFunction

defer块的位置应该放在花括号内的任何位置,并且总是在 return 语句之前,出于逻辑原因,也是为了避免警告:“'return' 之后的代码将永远不会被执行”。

一些例子:

在此处输入图片说明

在此处输入图片说明

我认为您需要将流程代码移至另一个功能。

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)
}

没有语言原语可以在 return 语句之后运行任意代码。 没有语言提供这一点。 但是,您始终可以使用闭包来嵌入和排序代码流; 就像一个完成处理程序。

在某些情况下,您可能想要使用willSet和/或didSet 想象一下,有一个 String 属性作为文本字段的后备存储。 如果字符串经过验证,则写入后备存储。 然后将触发willSet ,它可以运行您的process代码并根据结果直接更新textField

你的问题的答案是否定的。 但是解决您的问题很简单。

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”。 请参阅延迟性能部分。

延迟注入应该在代码的可达语句处,否则不会在块的末尾执行。 基本上,它的主要思想是延迟。

一旦函数返回,该函数的调用生命就结束了。 为了保证在函数返回后首先执行某事,需要调用该函数的对象在序列化环境中执行该某事。

但是,在您的用例中,该函数是由用户输入调用的,除非我们对它进行子类化或混合处理,否则我在您的用例中建议的是异步调度,延迟不明显:

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

这是一个边缘黑客,我个人不喜欢这种解决方案,但替代方案非常丑陋,从用户的角度来看,它看起来是无缝的。 工程中绝对有启发式的空间,这就是一个这样的例子。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM