泛型类不会将委托调用转发给具体的子类

时间:2017-02-06 19:51:35

标签: ios swift generics swift3

鉴于以下内容。

protocol EntityType {
    var displayString: String { get }
}

extension String: EntityType {
    var displayString: String { return self }
}

class GenericListViewController<Entity>: UIViewController, UITableViewDataSource, UITableViewDelegate where Entity: EntityType {
    let items: [Entity]

    let tableView: UITableView

    init(items: [Entity]) {
        self.items = items
        self.tableView = UITableView()

        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func loadView() {
        super.loadView()

        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)

        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
        ])

        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        tableView.dataSource = self
        tableView.delegate = self

    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.textLabel?.text = items[indexPath.row].displayString

        return cell
    }

//    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//        
//    }
}

class StringListViewController: GenericListViewController<String> {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("selected: \(items[indexPath.row])")
    }
}

为什么在具体的子类中调用tableView:didSelectRowAt:?这适用于非泛型类型,或者当父类具有实现并且子类覆盖它时。这是预期的行为,Swift bug还是我错过了什么?

1 个答案:

答案 0 :(得分:1)

这似乎是一个可能的错误。我猜它可能与Swift泛型对Objective-C运行时不可见的事实有关,所以即使直接在GenericListViewController<Entity>中实现的方法被调用,也可能存在一些错误的行为。 Swift和Obj-C运行时试图找出覆盖。绝对值得一个错误报告。

我会注意到,在严格的OOP中,抽象超类通常不符合协议本身,它们只是提供默认实现。仍然需要具体的子类来声明协议一致性并填写任何缺少的实现。

对于上面的代码,您的GenericListViewController<Entity>课程不应符合UITableViewDataSourceUITableViewDelegate。它只是提供了默认的方法实现,它允许具体的子类符合,而不必重写那些方法实现(除非需要覆盖)。您的StringListViewController应该是声明符合这两个协议的那个。如果您修改代码来执行此操作,它实际上将按预期工作。

但这并没有改变你可能在Swift / Obj-C互操作中发现错误的事实。我相信你目前应该的工作,虽然它不是严格的OOP方式来处理协议一致性。