我是一个快速的初学者,所以要温柔......
我将函数指定为参数时遇到问题.
我已经定义了这个结构:
struct dispatchItem {
let description: String
let f: ()->Void
init(description: String, f: @escaping ()->()) {
self.description = description
self.f = f
}
}
Run Code Online (Sandbox Code Playgroud)
我在一个名为MasterDispatchControllerlike 的类中使用它:
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
}
Run Code Online (Sandbox Code Playgroud)
等等
然后我在我的代码中有一个表视图,它发送到任何被点击的函数(不仅仅是我在上面的代码中显示的两个,但这不重要)就像这样
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dispatchItems[indexPath.row].f()
}
Run Code Online (Sandbox Code Playgroud)
所以...编译器对此并不满意.它说当我定义dispatchItems let语句时:
无法将类型'(MasterDispatchController) - >() - >()'的值转换为预期的参数类型'() - >()'
我想......好吧......我不确定我是否完全理解这一点,但似乎编译器想要知道回调函数将来自哪个类.我明白为什么它可能需要它.所以我有点盲目地遵循编译器给我的模式,并将我的结构更改为:
struct dispatchItem {
let description: String
let f: (MasterDispatchController)->()->Void
init(description: String, f: @escaping (MasterDispatchController)->()->()) {
self.description = description
self.f = f
}
}
Run Code Online (Sandbox Code Playgroud)
伟大的编译器很高兴,但现在当我尝试用dispatchItems[indexPath.row].f()它调用函数时说:
呼叫中缺少参数#1
该功能没有参数,所以我感到困惑......
我想也许是在问我有问题的对象的实例会有些意义......在我的例子中,这将是"自我",所以我尝试了dispatchItems[indexPath.row].f(self)然后我得到了一个错误:
表达式解析为未使用的函数
所以我有点卡住了.
对不起,如果这是一个愚蠢的问题.谢谢你的帮助.
问题是您试图在完全初始化之前testStaticTable在testEditableTable实例属性的初始化程序中引用实例方法。因此,编译器不能部分应用这些方法作为隐式参数,而只能为您提供类型为 \xe2\x80\x93 的柯里化版本。 selfself(MasterDispatchController) -> () -> ()
然后,人们可能会试图将dispatchItems属性标记为lazy,以便属性初始化程序在完全初始化时在属性的第一次访问时运行。self
class MasterDispatchController : UITableViewController {\n\n lazy private(set) var dispatchItems: [DispatchItem] = [\n DispatchItem(description: "Static Table", f: self.testStaticTable),\n DispatchItem(description: "Editable Table", f: self.testEditableTable)\n ]\n\n // ...\n}\nRun Code Online (Sandbox Code Playgroud)\n\n(请注意,我重命名了您的结构以符合 Swift 命名约定)
\n\n现在可以编译了,因为您现在可以引用方法的部分应用版本(即类型() -> Void),并且可以将它们调用为:
dispatchItems[indexPath.row].f()\nRun Code Online (Sandbox Code Playgroud)\n\n但是,您现在有一个保留周期,因为您正在存储self强捕获的闭包self。这是因为当用作值时,self.someInstanceMethod解析为强烈捕获 的部分应用闭包self。
对此的一种解决方案(您已经接近实现)是使用方法 \xe2\x80\x93 的柯里化版本,该版本不会强烈捕获self,而是必须应用于给定实例才能进行操作在。
struct DispatchItem<Target> {\n\n let description: String\n let f: (Target) -> () -> Void\n\n init(description: String, f: @escaping (Target) -> () -> Void) {\n self.description = description\n self.f = f\n }\n}\n\nclass MasterDispatchController : UITableViewController {\n\n let dispatchItems = [\n DispatchItem(description: "Static Table", f: testStaticTable),\n DispatchItem(description: "Editable Table", f: testEditableTable)\n ]\n\n override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {\n dispatchItems[indexPath.row].f(self)()\n }\n\n func testEditableTable() {}\n func testStaticTable() {}\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这些函数现在将给定实例作为MasterDispatchController参数,并返回正确的实例方法来调用该给定实例。因此,您需要首先使用 , 来应用它们self,以便f(self)获取要调用的实例方法,然后使用 调用结果函数()。
尽管不断地应用这些功能可能会很不方便self(或者您甚至可能无法访问self)。更通用的解决方案是将其存储self为 的weak属性DispatchItem,以及柯里化函数 \xe2\x80\x93 ,然后您可以“按需”应用它:
struct DispatchItem<Target : AnyObject> {\n\n let description: String\n\n private let _action: (Target) -> () -> Void\n weak var target: Target?\n\n init(description: String, target: Target, action: @escaping (Target) -> () -> Void) {\n self.description = description\n self._action = action\n }\n\n func action() {\n // if we still have a reference to the target (it hasn\'t been deallocated),\n // get the reference, and pass it into _action, giving us the instance\n // method to call, which we then do with ().\n if let target = target {\n _action(target)()\n }\n }\n}\n\nclass MasterDispatchController : UITableViewController {\n\n // note that we\'ve made the property lazy again so we can access \'self\' when\n // the property is first accessed, after it has been fully initialised.\n lazy private(set) var dispatchItems: [DispatchItem<MasterDispatchController>] = [\n DispatchItem(description: "Static Table", target: self, action: testStaticTable),\n DispatchItem(description: "Editable Table", target: self, action: testEditableTable)\n ]\n\n override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {\n dispatchItems[indexPath.row].action()\n }\n\n func testEditableTable() {}\n func testStaticTable() {}\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这可以确保您没有保留周期,因为DispatchItem没有对self.
当然,您可以使用unowned对 的引用self,如本问答中所示。但是,只有当您可以保证您的DispatchItem实例不会过时self(您希望为其创建dispatchItems一个private属性)时,您才应该这样做。
| 归档时间: |
|
| 查看次数: |
882 次 |
| 最近记录: |