1. Using Callback
2. Using Delegation
3. Using Notification
這篇文章會探討三種方法的優缺點,在看完後你可以選擇其中一個最符合你專案的方法。
1. Callback
建立Controller 跟 DataModel
class ViewController: UIViewController {
}
class DataModel {
}
1.1 Callback as a completion handler
在 DataModel宣告一個方法requestData
func requestData(completion: ((_ data: String) -> Void)){
// 假資料
let data = "Data from wherever"
completion(data)
}
// completion 是一個方法,傳回值是Void跟有一個字串參數data
class ViewController: UIViewController {
private let dataModel = DataModel()
override func viewDidLoad() {
super.viewDidLoad()
dataModel.requestData { [weak self] (data: String) in
self?.useData(data: data)
}
}
private func useData(data: String) {
print(data)
}
}
在ViewController中宣告一個DataModel,requestData傳入一個定義好的useData方法,當呼叫requestData在requestData中資料相關code處理好後,執行completion,把資料傳給controller也就是useData 方法。
1.2 Callback as a class property
用另一種方法利用Property處理Callback
宣告一個屬性onDataModelUpdate
class DataModel {
var onDataUpdate: ((_ data: String) -> Void)?
}
requestData方法中不需要參數,把completion改成剛剛的onDataModelUpdate
func requestData(){
// 假資料
let data = "Data from wherever"
onDataUpdate?(data)
}
在ViewController中給onDataUpdate assign相應的方法
class ViewController: UIViewController {
private let dataModel = DataModel()
override func viewDidLoad() {
super.viewDidLoad()
dataModel.onDataUpdate = { [weak self] (data: String) in
self?.useData(data: data)
}
dataModel.requestData()
}
}
你也可以建立多個callbacks,例如onDataUpdate、onHttpError等...所有的callbacks都是optional的,假如你不需要onHttpError,你就不要assign給它任何method就好,比起第一種方法有更好的彈性。
2. Delegation
宣告一個Protocol
protocol DataModelDelegate: class {
func didRecieveDataUpdate(data: String)
}
(特別注意到這邊的protocol用Class的原因是,之後我們要宣告delegate的變數為weak,如果沒限制為Class就不能宣告為weak,因為有可能是value type去實作這個protocol,不能宣告為weak就有可能造成retain cycle)
在DataModel 中宣告一個delegate,然後像用callback一樣
class DataModel {
weak var delegate: DataModelDelegate?
func requestData() {
// the data was received and parsed to String
let data = "Data from wherever"
delegate?.didRecieveDataUpdate(data: data)
}
}
在ViewController中實作Protocol
extension ViewController: DataModelDelegate {
func didRecieveDataUpdate(data: String) {
print(data)
}
}
在ViewContoller中assign 自己給Delegate,然後呼叫requestData
class ViewController: UIViewController {
private let dataModel = DataModel()
override func viewDidLoad() {
super.viewDidLoad()
dataModel.delegate = self
dataModel.requestData()
}
}
和Callback 方法相比,Delegation 更容易在整個App中重用,你可以建立一個base class 實作protocol delegate 避免重複的代碼。
Delegation的缺點:比較難實作,你需要創建一個protocol,宣告protocol methods,創建delegate property,assign delegate 給ViewController然後ViewController實作這個protocol,且必須實作protocol中的每個方法在ㄧ般的情況下。
3. Notification
什麼情況下會用到Notification
假設你有一個data你整個app都需要用到它,用delegation將需要每個ViewController都要實作,當然使用Callback或Delegation也可以,但使用Notification將更合適。
首先把DataModel修改為singleton class
class DataModel {
static var sharedInstance = DataModel()
private init() { }
}
然後宣告一個read-only local variable data
class DataModel {
static var sharedInstance = DataModel()
private init() { }
private (set) var data: String?
}
在requestData中接到的data assign 給local variable
func requestData() {
// the data was received and parsed to String
self.data = "Data from wherever"
}
宣告一個notification的識別字串(outside of model class)
let dataModelDidUpdateNotification = "dataModelDidUpdateNotification"
我們想在data updated的時候發送一個通知最好的方法是用property observer 在data local variable 中增加didSet property observer,然後post a notification
private (set) var data: String? {
didSet {
NotificationCenter.default.post(name:
NSNotification.Name(rawValue: dataModelDidUpdateNotification), object: nil)
}
}
如此只要當data值改變,我們就會post a notification,現在我們只要在每個要用到data 的ViewController中增加listener
class View Controller: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(getDataUpdate), name: NSNotification.Name(rawValue: dataModelDidUpdateNotification), object: nil)
DataModel.sharedInstance.requestData()
}
}
現在我們會監聽dataModel中的data值任何改變然後呼叫getDataUpdate方法,現在我們實作getDataUpdate方法
@objc private func getDataUpdate() {
if let data = DataModel.sharedInstance.data {
print(data)
}
}
跟Callback和Delegation比起來Notification事實上沒有從DataModel傳任何資料給Controller,而是像坐在家裡向Controller說:嘿~我這裡有新的data,可以進來抓囉。(有學過design pattern 觀察者模式應該很好懂)
當你處理notification時,永遠記得要remove當你不再使用時
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: dataModelDidUpdateNotification), object: self)
}
舉個例,例如你在使用navigationController,如果你的一個ViewController 在 navigation stack ,但它目前是不可見的,而第二個ViewController目前是可見的,當你想update第二個ViewController,第一個ViewController如果沒remove會跟著一起update,這是浪費資源的,可以在viewWillAppear中add然後在viewWillDisapear 中remove,確保你的ViewController只有在螢幕上顯示時才會監聽。
這篇主要是參考這篇寫的,如果英文好的朋友可以直接看這篇說的比較詳細
https://medium.com/ios-os-x-development/ios-three-ways-to-pass-data-from-model-to-controller-b47cc72a4336#.a62gb92tc
沒有留言:
張貼留言