微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Swift函数作为类中的参数

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

我将函数指定为参数时遇到问题.

我已经定义了这个结构:

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
    }

等等

然后我在我的代码中有一个表视图,它发送到任何被点击的函数(不仅仅是我在上面的代码显示的两个,但这不重要)就像这样

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

功能没有参数,所以我感到困惑……

我想也许是问我有问题的对象的实例会有一些意义……在我的例子中,这将是“自我”,所以我尝试了dispatchItems [indexPath.row] .f(self)但是后来我得到一个错误

Expression resolves to an unused function

所以我有点卡住了.

对不起,如果这是一个愚蠢的问题.谢谢你的帮助.

问题是,在self完全初始化之前,您尝试在实例属性的初始化器中引用实例方法testStaticTable和testEditableTable.因此,编译器不能将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命名约定)

现在可以编译,因为你现在可以参考方法的部分应用版本(即type() – > Void),并可以将它们称为:

dispatchItems[indexPath.row].f()

但是,你现在有一个保留周期,因为你在自我上存储了强大捕获自我的闭包.这是因为当用作值时,self.someInstanceMethod解析为部分应用的闭包,强烈捕获self.

您已经接近实现的一个解决方案是使用方法的curried版本 – 它们不会强烈捕获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: testEditableTable)
    ]

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

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

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

尽管不断地将这些功能应用于自己可能不方便(或者您甚至可能无法访问自己).更通用的解决方案是将自己存储为dispatchItem的弱属性,以及curried函数 – 然后您可以“按需”应用它:

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),action: testEditableTable)
    ]

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

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

这可确保您没有保留周期,因为dispatchItem没有对self的强引用.

当然,您可以使用自己的无主引用,如in this Q&A所示.但是,如果您可以保证您的dispatchItem实例不会超过自我(您希望将dispatchItem设为私有属性),则应该这样做一).

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐