为什么我们需要将委托设置为自我?为什么编译器不默认它?

Why do we need to set delegate to self? Why isn't it defaulted by the compiler?

认为我完全理解授权的概念,我的问题是当我们这样做时:

class someViewController : UIViewController, UITableViewDelegate{

}

我们是否曾经可能不想将tableView.delegate设置为self

如果没有任何机会,那为什么 Xcode 强迫我们在这里做一些额外的工作?

如果有可能将 tableView.delegate 设置为 self 以外的其他值...那是什么?能否提供一些例子?

would it ever be possible that we wouldn't want to set tableView.delegate to self

是的,如果您在自己的数据源和委托中实现 class。

why is Xcode forcing us to do some extra work here?

在我们的架构中允许尽可能多的自由。


数据源和委托的单独 classes 示例。

class TableViewDatasource: NSObject, UITableViewDataSource {
    // implement datasource
}

class TableViewDelegate : NSObject, UITableViewDelegate {
    // implement delegate
}

class ViewController: UIViewController {

     IBOutlet weak var tableView: UITableView! {
         didSet {
              tableView.dataSource = tableViewDatasource
              tableView.delegate = tableViewDelegate
         }
     }

     let tableViewDatasource = TableViewDatasource()
     let tableViewDelegate = TableViewDelegate()

}

如果您允许视图控制器持有委托和数据源的不同实现,这将允许更高的可重用性并有利于组合而不是继承。

你处理的是更小的 classes,它们更容易测试和维护。

甚至可以设计完整的应用程序,不需要任何视图控制器子classes。


datasource/delegate设计可以随心所欲。

作为示例,我想向您展示我的一个新项目:TaCoPopulator。它是一个框架,通过将其拆分为不同的任务以遵循 SOLID 原则,透明地填充 table 视图和集合视图:

import UIKit

class CollectionViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!
    private var datasource: ViewControllerDataSource<UICollectionView>?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.datasource = ViewControllerDataSource(with: collectionView)
    }
}

import UIKit

class TableViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    private var datasource: ViewControllerDataSource<UITableView>?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.datasource = ViewControllerDataSource(with: tableView)
    }
}

import TaCoPopulator


class IntDataProvider: SectionDataProvider<Int> {

    override init(reuseIdentifer: @escaping (Int, IndexPath) -> String) {
        super.init(reuseIdentifer: reuseIdentifer)
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
            self.provideElements([1,2,3,4,5,6,7,8,9])
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
                self.provideElements(self.elements() + [10, 11, 12, 13, 14, 15])
            }
        }
    }
}

import TaCoPopulator


class ViewControllerDataSource<TableOrCollectionView: PopulatorView> {
    init(with populatorView: PopulatorView) {
        self.populatorView = populatorView
        setup()
    }

    var intSelected: ((Int, IndexPath) -> Void)?
    var stringSelected: ((String, IndexPath) -> Void)?

    let dp1 = IntDataProvider {
        _ in return "Cell"
    }

    let dp2 = StringDataProvider {
        _ in return "Cell"
    }


    weak var populatorView: PopulatorView?
    var populator: Populator<MyViewPopulator>?

    func setup(){
        dp1.selected = {
            [weak self] element, indexPath in
            self?.intSelected?(element, indexPath)
        }

        dp2.selected = {
            [weak self] element, indexPath in
            self?.stringSelected?(element, indexPath)
        }

        let collectionViewCellConfig: (Any, TextCollectionViewCell, IndexPath) -> TextCollectionViewCell  = {
            element, cell, _ in
            cell.textLabel?.text = "\(element)"
            return cell
        }

        let tableViewViewCellConfig: (Any, UITableViewCell, IndexPath) -> UITableViewCell  = {
            element, cell, _ in
            cell.textLabel?.text = "\(element)"
            return cell
        }

        if  let populatorView  = populatorView as? UICollectionView {
            let  section1factory = SectionCellsFactory<Int, TextCollectionViewCell>(parentView: populatorView, provider: dp1, cellConfigurator: collectionViewCellConfig)
            let  section2factory = SectionCellsFactory<String, TextCollectionViewCell>(parentView: populatorView, provider: dp2, cellConfigurator: collectionViewCellConfig)
            self.populator = Populator(with: populatorView, sectionCellModelsFactories: [section1factory, section2factory])
        }

        if  let populatorView  = populatorView as? UITableView {
            let section1factory = SectionCellsFactory<Int, UITableViewCell>(parentView: populatorView, provider: dp1, cellConfigurator: tableViewViewCellConfig)
            let section2factory = SectionCellsFactory<String, UITableViewCell>(parentView: populatorView, provider: dp2, cellConfigurator: tableViewViewCellConfig)
            self.populator = Populator(with: populatorView, sectionCellModelsFactories: [section1factory, section2factory])
        }
    }

}

would it ever be possible that we wouldn't want to set tableView.delegate to self?

什么是 tableView.delegate?您的 class someViewController 不是 UITableViewController 的子 class(并且没有 tableView 属性)。它甚至不知道您在处理 table 视图。

您的类型声明符合 UITableViewDelegate 的事实并不能说明应该在哪个实际 table 视图实例上设置它。

If there is a chance that tableView.delegate is set to something other than self...well what is that?

通常将功能拆分为多种类型以降低视图控制器的复杂性是明智的。您的 someViewController 可能 有一个 viewModel 属性 来处理与 table 视图有关的所有事情。

当在想要的 ViewController 中提到 tableView.delegate = selftableView.dataSource = self 时,这意味着 ViewController 在说 "I am responsible for implementing those delegation/dataSource methods",这意味着 [=44] =] (self) 负责提供所需的方法让 tableView 知道它应该如何 looks/behaves.

关于您的问题:

would it ever be possible that we wouldn't want to set tableView.delegate to self?

实际上这是可能的,但这会导致 tableView 显示为空 tableView(其中没有行),因为没有人告诉它应该如何 looks/behave。

If there is a chance that tableView.delegate is set to something other than self...well what is that? Can you please provide some examples?

是的,tableView。dataSource/delegate 不必分配给包含此 tableView 的同一个 Viewcontroller(但我发现它更具可读性和可理解性)。

例如:

在下面的代码片段中,我将 tableView 的数据源分配给另一个 class(甚至不是 UIViewController)在不同的 。swift 文件,它完全可以正常工作:

import UIKit

// ViewController File
class ViewController: UIViewController {
    var handler: Handler!

    @IBOutlet weak var tableView: UITableView!
    override func viewDidLoad() {
        super.viewDidLoad()

        handler = Handler()
        tableView.dataSource = handler
    }
}

处理程序Class:

import UIKit

class Handler:NSObject, UITableViewDataSource {
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("myCell")

        cell?.textLabel?.text = "row #\(indexPath.row + 1)"

        return cell!
    }
}

输出工作正常。