寫作初衷:看了好多關于KVC/KVO/通知的相關文章,發現都是講理論,并且不太好理解,更不太好應用到自己的項目中。我自己總結了下它們三個,分別附了一個例子,希望對大家有所幫助~
一、KVC:Swift中使用KVC和KVO的類都必須必須繼承自NSObject
iOS很多UI控件會有很多隱藏的屬性,但是這些屬性,我們是可以通過kvc方式用的,不會被蘋果拒絕,善于使用隱藏的屬性,可以提高開發效率,其實kvc也是基于runtime實現的。
查看隱藏屬性的方式:
一:可以通過runtime打印出所有的隱藏屬性。
二:也可以用打斷點的方式,在調試區查看UI控件的隱藏屬性
// 用runtime的方式打印出所有UITextView的屬性
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([UITextView class], &count);
for (int i = 0; i<count; i++) {
Ivar ivar = ivars[i];
NSLog(@"UITextView-屬性名:-->%s------%s", ivar_getName(ivar));
}
kvc方式給對象的隱藏屬性賦值:
textField.placeholder = "請輸入名字"
// forKey: 不支持屬性的屬性。最好用下面的方法:forKeyPath:
// textField.setValue(UIColor.red, forKey: "_placeholderLabel.textColor")
這句會報錯:'[<UITextField 0x105bdf2a0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key _placeholderLabel.textColor.'
// 正確的方式:
textField.setValue(UIColor.red, forKeyPath: "_placeholderLabel.textColor")
備注:即使是forKeyPath的方式,里面的屬性也不能寫一個沒有的屬性,否則還是會崩潰。:invalid mode 'kCFRunLoopCommonModes' provided to CFRunLoopRunSpecific - break on _CFRunLoopError_RunCalledWithInvalidMode to debug. This message will only appear once per execution.
libc++abi.dylib: terminate_handler unexpectedly threw an exception
二、KVO: 當被觀察者的對象的屬性被修改的時候,會通知觀察者,KVC是KVO的基礎
// WKWebView監聽title (webView初始化的地方監聽)
webView.addObserver(self, forKeyPath: "title", options: .new, context: nil)
// 監聽title的回調
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if (keyPath == "title") {
self.addTitle(titleString: self.webView.title!)
}
}
deinit { // 必須在頁面銷毀的時候,remove
webView.removeObserver(self, forKeyPath: "title")
}
三、通知:需要被觀察者主動發送通知,然后觀察者注冊監聽,比KVO多了發送通知的一步。優點是:監聽不局限屬性的變化,可以對多種多樣的狀態變化進行監聽,范圍廣,靈活。
// 觸發通知的地方:
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationTagDic object:dic];
// 接收到通知的地方:
[[[[NSNotificationCenter defaultCenter] rac_addObserverForName:kNotificationTagDic object:nil] takeUntil:[self rac_willDeallocSignal]] subscribeNext:^(id x) {
NSDictionary *dic = [x object];
if (dic && [dic isKindOfClass:[NSDictionary class]] && dic.count > 0) {
}
}];