StoryBoard / Interface Builder中NSObject的功能是什么?

时间:2018-08-27 10:32:30

标签: ios swift testing tdd nsobject

我目前正在学习有关在Swift中测试iOS开发的视频教程,但是在View Controller中测试Table View时,我陷入了困境,因为我不明白为什么在Interface builder中需要NSObject如下图所示:

电影库数据服务继承了NSObject类:

enter image description here

MovieLibraryDataService的类是这样的:

import UIKit

class MovieLibraryDataService: NSObject, UITableViewDataSource, UITableViewDelegate {

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return UITableViewCell()
    }


}

MovieLibraryDataService类将在XCTestCase中使用,就像这样:

@testable import FilmFest
class LibraryViewControllerTests: XCTestCase {

    var sut: LibraryViewController!

    override func setUp() {
        super.setUp()
        // Put setup code here. This method is called before the invocation of each test method in the class.
        sut = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LibraryViewControllerID") as! LibraryViewController
        _ = sut.view
    }

    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }

    // MARK: Nil Checks
    func testLibraryVC_TableViewShouldNotBeNil() {
        XCTAssertNotNil(sut.libraryTableView)
    }

    // MARK: Data Source
    func testDataSource_ViewDidLoad_SetsTableViewDataSource() {
        XCTAssertNotNil(sut.libraryTableView.dataSource)
        XCTAssertTrue(sut.libraryTableView.dataSource is MovieLibraryDataService)
    }

    // MARK: Delegate
    func testDelegate_ViewDidLoad_SetsTableViewDelegate() {
        XCTAssertNotNil(sut.libraryTableView.delegate)
        XCTAssertTrue(sut.libraryTableView.delegate is MovieLibraryDataService)
    }

    // MARK: Data Service Assumptions
    func testDataService_ViewDidLoad_SingleDataServiceObject() {
        XCTAssertEqual(sut.libraryTableView.dataSource as! MovieLibraryDataService, sut.libraryTableView.delegate as! MovieLibraryDataService)
    }



}

以及LibraryViewController的定义:

import UIKit

class LibraryViewController: UIViewController {

    @IBOutlet weak var libraryTableView: UITableView!
    @IBOutlet var dataService: MovieLibraryDataService!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.libraryTableView.dataSource = dataService
        self.libraryTableView.delegate = dataService
    }


}

我真的不明白为什么我需要参加MovieLibraryDataService

我通常使用:

self.libraryTableView.dataSource = self
self.libraryTableView.delegate = self

但是我为什么要写:

self.libraryTableView.dataSource = dataService
self.libraryTableView.delegate = dataService

2 个答案:

答案 0 :(得分:0)

编辑

您可以将NSObject上的Storyboard用于不同的目的,其中之一也可以是委派。而不是像下面这样以编程方式设置它:

self.libraryTableView.dataSource = self
self.libraryTableView.delegate = self

您可以按住 control ,然后按如下所示设置相应的委托:

enter image description here

原始答案

因为MovieLibraryDataService符合UITableViewDataSourceUITableViewDelegate,而不是您的LibraryViewController

如果要像往常一样更改它,请将代码更改为:

class LibraryViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var libraryTableView: UITableView!
    var dataService = MovieLibraryDataService()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.libraryTableView.dataSource = self
        self.libraryTableView.delegate = self
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return UITableViewCell()
    }
}

我个人建议保持现有状态,因为 View Controllers 往往会增长并变得像我们称之为Massive View Controller

答案 1 :(得分:0)

MovieLibraryDataService只是另一个实现UITableViewDataSourceUITableViewDelegate的类,不同之处在于故事板将其实例化,并且storyboard-created实例是通过{{1}绑定的} IBOutlet。情节提要中的所有对象均为@IBOutlet var dataService: MovieLibraryDataService!,这就是为什么如果对象未绑定到您已经使用的其他变量,则必须将它们绑定到要使用的变量。

为变量storyboard-created命名只是一种幻想的说法,它应该被dataService使用,在这种情况下,它是tableView的serve,因为它实现了那些委托协议。

由于故事板实例化了dataSource and delegate,因此您可以尝试将故事板中的dataService绑定到dataService。这是可能的,因为情节提要中有tableView可引用。这将替换dataService中的设置dataSource and delegate

另一个例子

viewController也是情节提要中的AppDelegate,因此情节提要中的NSObject可以引用要使用的内容,从而避免了{@ 1}}的设置故事板。 (否则会很丑陋,也许只是macOS,因为Mac应用程序必须显示UIApplication/NSApplication,即使它为空。)