简体   繁体   中英

what is the function of NSObject in StoryBoard / Interface Builder?

I am currently following a video tutorial course about test driven development of iOS in Swift, but when testing Table View in View Controller, I get stuck, since I don't understand why we need NSObject in Interface builder like the picture below:

Movie Library Data Service is inheritted NSObject class:

在此处输入图片说明

the class of MovieLibraryDataService is like this:

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()
    }


}

and the MovieLibraryDataService class will be used in XCTestCase is like this :

@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)
    }



}

and the definition of 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
    }


}

I really don't understand why I need to make that MovieLibraryDataService class

I usually use:

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

but why do I need to write :

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

Edit

You can use NSObject on the Storyboard for different purposes, one of them could be also the delegation. Instead of setting it programatically like:

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

you can hold control and then set the corresponding delegates like below:

在此处输入图片说明

Original Answer

Because MovieLibraryDataService that conforms to UITableViewDataSource and UITableViewDelegate and not your LibraryViewController .

If you want to change it as you are always used to, change your code to:

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()
    }
}

Personally I would suggest to keep it as you have it, as View Controllers tend to grow and become like we call it Massive View Controller

MovieLibraryDataService is just another class which implements UITableViewDataSource and UITableViewDelegate , with the difference that the storyboard instantiates it and that storyboard-created instance is bound via the IBOutlet @IBOutlet var dataService: MovieLibraryDataService! . All objects in the storyboard are storyboard-created , that's why one has to bind them to variables to use if they are not bound to other variables you use already.

Naming the variable dataService is just a fancy way of saying that it is supposed to serve , in this case as dataSource and delegate for a tableView, since it implements those delegate-protocols.

Since the dataService is instantiated by the storyboard, you could try if you can bind the dataService inside the storyboard to tableView . This is possible because you have the dataService referencable in the storyboard. This would replace setting dataSource and delegate in the viewController .

Another example

AppDelegate is also an NSObject inside the Storyboard so the UIApplication/NSApplication inside the storyboard is able to reference that to use, avoids having to set up AppDelegate yourself outside a storyboard. (Would otherwise be ugly, maybe that's macOS only though, since mac apps have to show a Menu even if its empty.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM