gpt4 book ai didi

Swift 函数作为类中的参数

转载 作者:行者123 更新时间:2023-11-30 12:26:52 25 4
gpt4 key购买 nike

我是一个快速的初学者,所以要温柔......

我在将函数分配为参数时遇到问题。

我已经定义了这个结构:

struct dispatchItem {
let description: String
let f: ()->Void

init(description: String, f: @escaping ()->()) {
self.description = description
self.f = f
}
}

我在一个名为 MasterDispatchController 的类中使用它,如下所示:

class MasterDispatchController: UITableViewController {

let dispatchItems = [
dispatchItem(description: "Static Table", f: testStaticTable),
dispatchItem(description: "Editable Table", f: testEditableTable)
]

func testEditableTable() {
//some code
}

func testStaticTable() {
//some code
}

等等

然后我的代码中有一个 TableView ,它分派(dispatch)到单击的任何函数(不仅仅是上面代码中显示的两个函数,但这并不重要),就像这样

   override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].f()
}

所以...编译器对此不满意。它说当我定义dispatchItems let语句时:

Cannot convert value of type '(MasterDispatchController) -> () -> ()' to expected argument type '() -> ()'

我想...好吧...我不确定我是否完全理解这一点,但编译器似乎想知道回调函数将来自哪个类。我明白为什么它可能需要这个。所以我有点盲目地遵循编译器给我的模式,并将我的结构更改为:

struct dispatchItem {
let description: String
let f: (MasterDispatchController)->()->Void

init(description: String, f: @escaping (MasterDispatchController)->()->()) {
self.description = description
self.f = f
}
}

太棒了,编译器很高兴,但现在当我尝试使用 dispatchItems[indexPath.row].f() 调用该函数时,它说:

Missing parameter #1 in call

该函数没有参数,所以我很困惑......

我想它可能是在询问我所讨论的对象的实例,这会有意义......在我的示例中这将是“self”,所以我尝试了 dispatchItems[indexPath.row].f (self) 但后来我得到了一个错误:

Expression resolves to an unused function

所以我有点陷入困境。

抱歉,如果这是一个愚蠢的问题。感谢您的帮助。

最佳答案

问题是您试图在实例属性的初始化程序之前中引用实例方法testStaticTabletestEditableTable self 已完全初始化。因此,编译器无法以 self 作为隐式参数来部分应用这些方法,而只能 offer you the curried versions – 类型为 (MasterDispatchController) -> () -> ()

人们可能会想将 dispatchItems 属性标记为 lazy ,以便当 self 完全初始化时,属性初始化程序在属性首次访问时运行。

class MasterDispatchController : UITableViewController {

lazy private(set) var dispatchItems: [DispatchItem] = [
DispatchItem(description: "Static Table", f: self.testStaticTable),
DispatchItem(description: "Editable Table", f: self.testEditableTable)
]

// ...
}

(请注意,我重命名了您的结构以符合 Swift 命名约定)

现在可以编译了,因为您现在可以引用方法的部分应用版本(即类型 () -> Void),并且可以将它们调用为:

dispatchItems[indexPath.row].f()

但是,您现在有一个保留周期,因为您将闭包存储在 self 上,而该闭包强烈捕获 self。这是因为当用作值时,self.someInstanceMethod 解析为强烈捕获 self 的部分应用闭包。

解决此问题的一个解决方案(您已经接近实现)是使用方法的柯里化(Currying)版本 - 它强烈捕获self,但必须应用于给定的实例才能进行操作。

struct DispatchItem<Target> {

let description: String
let f: (Target) -> () -> Void

init(description: String, f: @escaping (Target) -> () -> Void) {
self.description = description
self.f = f
}
}

class MasterDispatchController : UITableViewController {

let dispatchItems = [
DispatchItem(description: "Static Table", f: testStaticTable),
DispatchItem(description: "Editable Table", f: testEditableTable)
]

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].f(self)()
}

func testEditableTable() {}
func testStaticTable() {}
}

这些函数现在将给定的 MasterDispatchController 实例作为参数,并返回正确的实例方法来调用该给定实例。因此,您需要首先使用 self 应用它们,通过说 f(self) 来获取要调用的实例方法,然后使用 调用结果函数>().

尽管不断地使用 self 应用这些函数可能会很不方便(或者您甚至可能无法访问 self)。更通用的解决方案是将 self 存储为 DispatchItem 上的 weak 属性,以及柯里化(Currying)函数 - 然后您可以将其应用到-需求':

struct DispatchItem<Target : AnyObject> {

let description: String

private let _action: (Target) -> () -> Void
weak var target: Target?

init(description: String, target: Target, action: @escaping (Target) -> () -> Void) {
self.description = description
self._action = action
}

func action() {
// if we still have a reference to the target (it hasn't been deallocated),
// get the reference, and pass it into _action, giving us the instance
// method to call, which we then do with ().
if let target = target {
_action(target)()
}
}
}

class MasterDispatchController : UITableViewController {

// note that we've made the property lazy again so we can access 'self' when
// the property is first accessed, after it has been fully initialised.
lazy private(set) var dispatchItems: [DispatchItem<MasterDispatchController>] = [
DispatchItem(description: "Static Table", target: self, action: testStaticTable),
DispatchItem(description: "Editable Table", target: self, action: testEditableTable)
]

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].action()
}

func testEditableTable() {}
func testStaticTable() {}
}

这可以确保您没有保留周期,因为 DispatchItem 没有对 self 的强引用。

当然,您可以使用对 selfunowned 引用,例如 in this Q&A 。但是,只有当您可以保证您的 DispatchItem 实例不会比 self 生命周期更长(您希望使 dispatchItems 是一个 private 属性)。

关于Swift 函数作为类中的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44076537/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com