Swift 和@objc 方法:如何转换方法以便它可以由@objc 表示?
Swift and @objc methods: How do I transform a method so that it can be represented by @objc?
由于 Swift 是我的第一门编程语言,而且我还没有 Objective C 经验...
我很难理解@objc 与方法的关系。
如何使用@objc 语法来符合我的方法?
是否有另一种方法 select 不使用 #selector 语法的方法?
这是我遇到困难的代码(主要是 @objc 对 startGame 方法的尝试):
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()
}
我认为您的@objc 方法有语法错误。应该是:
@objc
func functionName() {
}
对你来说它将是:
@objc
func startGame() {
title = allWords.randomElement()
usedWords.removeAll(keepingCapacity: true)
tableView.reloadData()
}
几点观察:
您的视图控制器声明中不需要 @objc
。
两个action/selector方法应该带有@objc
限定符。
我建议您为这两个方法起一个描述性的名称,清楚地表明它们在用户点击特定按钮时被调用,例如:
@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(_:)))
小心放置牙套。 Swift 允许您在函数内部声明函数。因此,请确保这些 select 或方法是视图控制器的实例方法,而不是例如在另一个函数(即 viewDidLoad
)中声明的私有函数。
如果你开始忘记大括号,你可以select这个文件中的所有代码然后按control+i(或在 Xcode 菜单中,“编辑器”»“结构”»“Re-Indent”)。如果你在某处缺少大括号,代码的 re-indentation 会让你跳出来。
所以把它们放在一起,你会得到类似的东西:
// 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 { ![=12=].isEmpty }
}
func startGame() {
title = allWords.randomElement()
usedWords.removeAll(keepingCapacity: true)
tableView.reloadData()
}
}
对我的代码示例的一些最终观察(与您的问题没有直接关系,但只是为了解释为什么像我那样构建它):
我喜欢把方法放在扩展中,这样它们就在逻辑组中。这样可以更轻松地一目了然地了解正在发生的事情。您还可以 collapse/expand 这些扩展,以便您在编辑时可以专注于相关代码。
MARK
注释只是将不错的部分 headers 放在 Xcode 跳转栏中,再次让您更容易在代码中跳转。
除了用“业务逻辑”调用某些方法外,我个人不会在操作方法中放置任何东西。这将“视图”代码(按钮的处理)与业务逻辑分开。有一天,您可能会开始使用视图模型或演示器 objects,因此现在接受这种职责分离将使最终的过渡更容易。当您着手编写单元测试时,它也将使编写单元测试变得更容易(例如,您为“开始游戏”逻辑编写单元测试,而不是点击按钮)。
由于 Swift 是我的第一门编程语言,而且我还没有 Objective C 经验... 我很难理解@objc 与方法的关系。
如何使用@objc 语法来符合我的方法? 是否有另一种方法 select 不使用 #selector 语法的方法?
这是我遇到困难的代码(主要是 @objc 对 startGame 方法的尝试):
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()
}
我认为您的@objc 方法有语法错误。应该是:
@objc
func functionName() {
}
对你来说它将是:
@objc
func startGame() {
title = allWords.randomElement()
usedWords.removeAll(keepingCapacity: true)
tableView.reloadData()
}
几点观察:
您的视图控制器声明中不需要
@objc
。两个action/selector方法应该带有
@objc
限定符。我建议您为这两个方法起一个描述性的名称,清楚地表明它们在用户点击特定按钮时被调用,例如:
@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(_:)))
小心放置牙套。 Swift 允许您在函数内部声明函数。因此,请确保这些 select 或方法是视图控制器的实例方法,而不是例如在另一个函数(即
viewDidLoad
)中声明的私有函数。如果你开始忘记大括号,你可以select这个文件中的所有代码然后按control+i(或在 Xcode 菜单中,“编辑器”»“结构”»“Re-Indent”)。如果你在某处缺少大括号,代码的 re-indentation 会让你跳出来。
所以把它们放在一起,你会得到类似的东西:
// 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 { ![=12=].isEmpty }
}
func startGame() {
title = allWords.randomElement()
usedWords.removeAll(keepingCapacity: true)
tableView.reloadData()
}
}
对我的代码示例的一些最终观察(与您的问题没有直接关系,但只是为了解释为什么像我那样构建它):
我喜欢把方法放在扩展中,这样它们就在逻辑组中。这样可以更轻松地一目了然地了解正在发生的事情。您还可以 collapse/expand 这些扩展,以便您在编辑时可以专注于相关代码。
MARK
注释只是将不错的部分 headers 放在 Xcode 跳转栏中,再次让您更容易在代码中跳转。除了用“业务逻辑”调用某些方法外,我个人不会在操作方法中放置任何东西。这将“视图”代码(按钮的处理)与业务逻辑分开。有一天,您可能会开始使用视图模型或演示器 objects,因此现在接受这种职责分离将使最终的过渡更容易。当您着手编写单元测试时,它也将使编写单元测试变得更容易(例如,您为“开始游戏”逻辑编写单元测试,而不是点击按钮)。