Swift 和 @objc 方法:如何转换方法,使其可以用@objc表示?

Swift and @objc methods: How do I transform a method so that it can be represented by @objc?

提问人:Lio 提问时间:3/9/2021 最后编辑:Lio 更新时间:3/9/2021 访问量:175

问:

由于 Swift 是我的第一门编程语言,而且我没有 Objective C 经验...... 我很难理解与方法相关的@objc。

如何使用@objc语法来符合我的方法? 有没有另一种方法可以在不使用 #selector 语法的情况下选择方法?

这是我遇到困难的代码(主要是 startGame 方法的@objc尝试):

import UIKit

@objc class ViewController: UITableViewController {
var allWords = [String]()
var usedWords = [String]()


override func viewDidLoad() {
    super.viewDidLoad()
    
navigationItem.rightBarButtonItem = 
UIBarButtonItem(barButtonSystemItem: .add, target: self, action: 
#selector(promptForAnswer))
    
    navigationItem.leftBarButtonItem = UIBarButtonItem(title: "New 
Word", style: .plain, target: self, action: #selector(startGame))
   
    
    if let startWordsURL = Bundle.main.url(forResource: "start", 
withExtension: "txt") {
        if let startWords = try? String(contentsOf: startWordsURL) {
            allWords = startWords.components(separatedBy: "\n")
        }
    }
    if allWords.isEmpty {
        allWords = ["silkworm"]
    }
    
@objc func startGame() {
        title = allWords.randomElement()  
        usedWords.removeAll(keepingCapacity: true) 
        tableView.reloadData() 
    {
    startGame()
    }
Objective-C SWIFT 函数 方法

评论

1赞 jnpdx 3/9/2021
你在这里省略了很多重要信息(或者我假设你是)。 需要与其他函数在同一级别进行定义 -- 当前代码使其看起来好像嵌套在另一个函数。如果是这样的话,那就是你的问题。如果不是这种情况,请向代码显示真实代码中显示的结构。startGame
0赞 Lio 3/9/2021
是的,我确实在那里省略了很多信息。我更新了代码。我不认为我的方法嵌套在这里。让我知道我是否应该包含更多代码。谢谢!!
2赞 jnpdx 3/9/2021
在我看来仍然像在里面.它需要成为它自己的功能,与它处于同一级别,而不是在它内部。func startGame()viewDidLoadviewDidLoad
0赞 McNail 3/9/2021
之间 和 应该是两个,而不是一个["silkworm"]@objc}}}

答:

-1赞 Umar Maikano 3/9/2021 #1

我认为您在@objc方法中有语法错误。它应该是:

@objc
func functionName() {
}

对你来说,这将是:

@objc
func startGame() {
        title = allWords.randomElement()  
        usedWords.removeAll(keepingCapacity: true) 
        tableView.reloadData() 
   }
2赞 Rob 3/9/2021 #2

几点观察:

  1. 您不需要在视图控制器声明中声明。@objc

  2. 两个操作/选择器方法应带有限定符。@objc

  3. 我建议你给这两个方法一个描述性的名称,清楚地表明它们在用户点击特定按钮时被调用,例如:

    @objc func didTapNewWord(_ sender: UIBarButtonItem) {
        ...
    }
    
    @objc func didTapAdd(_ sender: UIBarButtonItem) {
        ...
    }
    

    请注意,我还为这些方法添加了一个参数。这使得它们是按钮处理程序是完全明确的。您不需要这样做,但现在您可以浏览代码并立即了解该方法的用途。

    显然,您将相应地更改添加这些目标操作的代码:

    navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add,
                                                        target: self,
                                                        action: #selector(didTapAdd(_:)))
    
    navigationItem.leftBarButtonItem = UIBarButtonItem(title: "New Word",
                                                       style: .plain,
                                                       target: self,
                                                       action: #selector(didTapNewWord(_:)))
    
  4. 小心牙套的放置。Swift 允许您在函数中声明函数。因此,请确保这些选择器方法是视图控制器的实例方法,而不是在另一个函数(即 )。viewDidLoad

    如果您开始忘记大括号,您可以选择此文件中的所有代码并按 +(或在 Xcode 菜单中,“编辑器”»“结构”»“重新缩进”)。如果你在某处缺少大括号,代码的重新缩进会让你跳出来。controli


所以把它们放在一起,你会得到类似的东西:

//  ViewController.swift

import UIKit

class ViewController: UITableViewController {
    var allWords = [String]()
    var usedWords = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()

        configureButtons()
        fetchData()
    }
}

// MARK: - Actions

extension ViewController {
    @objc func didTapNewWord(_ sender: UIBarButtonItem) {
        startGame()
    }

    @objc func didTapAdd(_ sender: UIBarButtonItem) {
        ...
    }
}

// MARK: - UITableViewDataSource

extension ViewController {
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        ...
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        ...
    }
}

// MARK: - Private utility methods

private extension ViewController {
    func configureButtons() {
        navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add,
                                                            target: self,
                                                            action: #selector(didTapAdd(_:)))

        navigationItem.leftBarButtonItem = UIBarButtonItem(title: "New Word",
                                                           style: .plain,
                                                           target: self,
                                                           action: #selector(didTapNewWord(_:)))
    }

    func fetchData() {
        guard
            let startWordsURL = Bundle.main.url(forResource: "start", withExtension: "txt"),
            let startWords = try? String(contentsOf: startWordsURL).components(separatedBy: "\n"),
            !startWords.isEmpty
        else {
            allWords = ["silkworm"]
            return
        }

        allWords = startWords.filter { !$0.isEmpty }
    }

    func startGame() {
        title = allWords.randomElement()
        usedWords.removeAll(keepingCapacity: true)
        tableView.reloadData()
    }
}

关于我的代码示例的一些最后观察(与您的问题没有直接关系,但只是为了解释为什么像我一样构建它):

  • 我喜欢将方法放入扩展中,以便它们在逻辑组中。这样可以更轻松地一目了然地了解正在发生的事情。您还可以折叠/展开这些扩展,以便在编辑时可以专注于相关代码。

  • 这些注释只是在 Xcode 跳转栏中放置了漂亮的部分标题,再次使在代码中跳转变得更加容易。MARK

  • 我个人不会在操作方法中放置任何东西,除了使用“业务逻辑”调用某个方法。这将“视图”代码(按钮的处理)与业务逻辑分开。总有一天,您可能会开始使用视图模型或演示者对象,因此现在接受这种职责分离将使最终的转换更容易。当你开始编写单元测试时,它也会使编写单元测试变得更容易(例如,你为“开始游戏”逻辑编写单元测试,而不是点击按钮)。

评论

0赞 Lio 3/10/2021
太棒了,谢谢你的详细回复。
0赞 Lio 3/10/2021
我肯定需要更好地组织我的代码。感谢您的建议!