如何防止访问其他 类 的委托方法?

How to prevent accessing delegate methods for other classes?

为了制作 "more encapsulated" 应用程序,我正在尝试为我的视图控制器指定访问级别 properties/methods。但问题是当尝试 private 一个 datasource/delegate 方法时,我收到一个编译时错误抱怨它。

比如我有两个视图控制器:ViewControllerAViewControllerB,第一个实现了table视图数据源方法和privateWork()私有方法,如下:

class ViewControllerA: UIViewController, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 101
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell")!
        return cell
    }

    private func privateWork() {
        print(#function)
    }
}

第二个视图控制器有一个 ViewControllerA 的实例 - 在一个名为 setupViewControllerA() 的方法中 - 如下:

class ViewControllerB: UIViewController {
    func setupViewControllerA() {
        // in real case, you should get it from its stroyborad,
        // this is only for the purpose of demonstrating the issue...
        let vcA = ViewControllerA()

        vcA.privateWork()
    }
}

实现vcA.privateWork()时出现编译时错误:

'privateWork' is inaccessible due to 'private' protection level

太合适了!我想阻止其他 class 访问 vcA.privateWork()

我的问题是: 简单地说,我想对数据源方法执行相同的操作,所以如果我尝试实现:

private func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 101
}

我遇到编译时错误:

Method 'tableView(_:numberOfRowsInSection:)' must be as accessible as its enclosing type because it matches a requirement in protocol 'UITableViewDataSource'

有一个修正建议让 private 变成 internal。它导致以下代码(在第二个视图控制器中):

vcA.tableView(..., numberOfRowsInSection: ...)

要有效,这是不合适的,没有必要让这样的数据源方法可以从其 class 外部访问。

遇到这种情况怎么办?

制作 table 符合 UITableViewDataSourceUITableViewDelegate 的视图适配器,并将其作为私有 属性 添加到您的视图控制器:

final class TableViewAdapter: NSObject, UITableViewDataSource, UITableViewDelegate {

    // Implementation of protocol methods

}



class ViewControllerA: UIViewController {

    private let adapter = TableViewAdapter()

    @IBOutlet private var tableView: UITableView! {
        didSet {
            tableView.delegate = adapter
            tableView.dataSource = adapter
        }
    }

}

您无需担心委托和数据源方法的隐私问题,您仍然可以选择创建另一个 class 来处理您的 tableview 数据源内容

例如

class IceCreamListDataSource: NSObject, UITableViewDataSource
{
  let dataStore = IceCreamStore()

  // MARK: - Table view data source

  func numberOfSectionsInTableView(tableView: UITableView) -> Int
  {
    return 1
  }

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

  func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
  {
    let flavor = dataStore.allFlavors()[indexPath.row]
    let cell = tableView.dequeueReusableCellWithIdentifier("IceCreamListCell", forIndexPath: indexPath)
    cell.textLabel?.text = flavor
    return cell
  }
}

现在,在您的 ViewControllerUITableViewController class 中,您可以使用此 class 作为您的数据源

class IceCreamListViewController: UITableViewController
{
  let dataSource = IceCreamListDataSource()

  // MARK: - View lifecycle

  override func viewDidLoad()
  {
    super.viewDidLoad()
    tableView.dataSource = dataSource
  }
}

希望对您有所帮助