遵從 protocol 問題: does not conform to protocol NSObjectProtocol
開發 iOS App 時,我們時常需要讓自訂的型別遵從 iOS SDK 的 protocol,比方我們自訂的 view controller 為了在表格 cell 點選時執行某段程式,需要遵從 protocol UITableViewDataDelegate。
class MovieViewController: UIViewController, UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { }}
但如果我們使用沒有繼承任何人的類別遵從 protocol UITableViewDataDelegate 時,卻會產生莫名其妙的錯誤。
class MovieTableViewDelegate: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { }}
錯誤訊息提到 Cannot declare conformance to ‘NSObjectProtocol’ in Swift
。但是我們明明是遵從 UITableViewDataDelegate,不是 NSObjectProtocol 呀。
非也。你還不夠了解 UITableViewDataDelegate,就像彼得潘不懂虎克船長一樣。其實它遵從著 UIScrollViewDelegate,而 UIScrollViewDelegate 遵從著 NSObjectProtocol,所以我們遵從 UITableViewDataDelegate 時,也要負責將 UIScrollViewDelegate & NSObjectProtocol 的功能實現。
protocol UITableViewDelegate : UIScrollViewDelegateprotocol UIScrollViewDelegate : NSObjectProtocol
問題應該不大,Xcode 現在有貼心的 Fix 按鈕,只要點選 Fix,它會幫我們補上需要定義的 protocol 功能。
但是事實不然。如上圖所示,Xcode 幫我們加了一堆來自 NSObjectProtocol 的 function,而且我們毫無頭緒,不知道該在這些 function 裡寫什麼。
其實我們不需要一個個辛苦地實現 NSObjectProtocol 的功能,有一個更簡單的方法,我們只要讓自訂的類別繼承 NSObject。
class MovieTableViewDelegate: NSObject, UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { }}
NSObject 怎麼會如此厲害呢 ? 答案就藏在它的身世裡。原來它早已遵從 NSObjectProtocol,因此它已經幫我們定義 NSObjectProtocol 的功能。這也是為何當我們的類別繼承 iOS SDK 內建類別再遵從 UITableViewDataDelegate 時不會有問題,因為所有的 iOS SDK 內建類別都繼承自 NSObject。
class NSObject : NSObjectProtocol
另外在遵從 iOS SDK 的 protocol 時,還有個小地方要注意。我們只能使用 class 型別,像以下 struct 定義的型別就無法。
Non-class type 'MovieTableViewDelegate' cannot conform to class protocol 'UITableViewDelegate'
錯誤訊息提到 struct 是 Non-class type,無法遵從 class protocol。iOS SDK 裡的 protocol 都是 class protocol,註定只能被 class 型別遵從。