写作初衷:看了好多关于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) {
}
}];