引子,苹果提供了两种 KVO
第二种,挺方便的
1, 通过调用方法,addObserver(
- 监听的对象
class Cate: NSObject{
@objc dynamic var age = 51
var k: String{
"age"
}
}
class ViewController: UIViewController {
let cat = Cate()
}
- 添加观察者
cat.addObserver(self, forKeyPath: cat.k, options: .new, context: nil)
- 响应观察
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let dict = change{
print("age : ", dict[.newKey] ?? "ha ha")
}
}
- 移除观察者
cat.removeObserver(self, forKeyPath: cat.k, context: nil)
2,便捷的 closure
- 观察的行为,作为一个对象
var moneyObservation: NSKeyValueObservation?
- 添加观察者,闭包响应
被观察对象,见上文
直接写路径,不再是字符串硬编码 ( 较容易出错 )
moneyObservation = cat.observe(\.age, changeHandler: { cat, change in
print("money : ", cat.age)
})
- 移除观察者
moneyObservation = nil
进入 FBKVOController
, FB 自研部分
FBKVOController
对系统的 KVO 方法,有一个封装,
从苹果提供的 KVO 实现一,到 KVO 实现 2
和随着所属控制器的释放,自动移除观察
FBKVOController
思路跟一些三方库,非常一致
-
有一个单例
_FBKVOSharedController
,具体做事情的 -
有一个关联对象
FBKVOController
,动态将事件添加到该单例
( 还有,动态将事件,注销 )
FBKVOController
, 自个作为属性,创建使用,也可
FBKVOController
的具体实现
特点就是,把一般对控制器 ctrl 的观察,转化为对统一的单例对象的观察
添加观察者
- 先保存信息
@implementation FBKVOController
- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(FBKVONotificationBlock)block
{
if (nil == object || 0 == keyPath.length || NULL == block) {
return;
}
// 创建待保存的信息
// create info
_FBKVOInfo *info = [[_FBKVOInfo alloc] initWithController:self keyPath:keyPath options:options block:block];
// observe object with info
[self _observe:object info:info];
}
@end
- 实现观察者的转移,
一般是 ctrl , 观察对象的某个属性
转化为
单例 _FBKVOSharedController
,观察对象的某个属性
@implementation FBKVOController
- (void)_observe:(id)object info:(_FBKVOInfo *)info
{
// lock
pthread_mutex_lock(&_lock);
NSMutableSet *infos = [_objectInfosMap objectForKey:object];
// check for info existence
_FBKVOInfo *existingInfo = [infos member:info];
// 安全检查
if (nil != existingInfo) {
// observation info already exists; do not observe it again
// unlock and return
pthread_mutex_unlock(&_lock);
return;
}
// 使用信息集合
// lazilly create set of infos
if (nil == infos) {
infos = [NSMutableSet set];
[_objectInfosMap setObject:infos forKey:object];
}
// 添加信息
// add info and oberve
[infos addObject:info];
// unlock prior to callout
pthread_mutex_unlock(&_lock);
// 观察者的转移
[[_FBKVOSharedController sharedController] observe:object info:info];
}
@end
剩余的就是
单例 _FBKVOSharedController
,调用苹果的 KVO 实现一
响应变化
很全面
-
block
-
target - action
-
苹果的 KVO 实现一,的响应方法
@implementation _FBKVOSharedController
- (void)observeValueForKeyPath:(nullable NSString *)keyPath
ofObject:(nullable id)object
change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change
context:(nullable void *)context
{
_FBKVOInfo *info;
{
// lookup context in registered infos, taking out a strong reference only if it exists
pthread_mutex_lock(&_mutex);
info = [_infos member:(__bridge id)context];
pthread_mutex_unlock(&_mutex);
}
if (nil != info) {
// take strong reference to controller
FBKVOController *controller = info->_controller;
if (nil != controller) {
// 这里是 weak - strong dance 的第二步
// take strong reference to observer
id observer = controller.observer;
if (nil != observer) {
// dispatch custom block or action, fall back to default action
if (info->_block) {
NSDictionary<NSKeyValueChangeKey, id> *changeWithKeyPath = change;
// add the keyPath to the change dictionary for clarity when mulitple keyPaths are being observed
if (keyPath) {
NSMutableDictionary<NSString *, id> *mChange = [NSMutableDictionary dictionaryWithObject:keyPath forKey:FBKVONotificationKeyPathKey];
[mChange addEntriesFromDictionary:change];
changeWithKeyPath = [mChange copy];
}
// block 响应
info->_block(observer, object, changeWithKeyPath);
} else if (info->_action) {
// target - action
[observer performSelector:info->_action withObject:change withObject:object];
} else {
// 苹果的 KVO 实现一,的响应方法
[observer observeValueForKeyPath:keyPath ofObject:object change:change context:info->_context];
}
}
}
}
}
@end
自动释放
FBKVOController 的对象,作为 ctrl 的属性,
ctrl 释放,他的属性 FBKVOController 对象,释放
调用 FBKVOController 的 dealloc
方法
@implementation FBKVOController
// 触发,释放
- (void)dealloc
{
[self unobserveAll];
pthread_mutex_destroy(&_lock);
}
// 具体的释放逻辑
// 两层, FBKVOController 中
// _FBKVOSharedController 中
- (void)_unobserveAll
{
// lock
pthread_mutex_lock(&_lock);
NSMapTable *objectInfoMaps = [_objectInfosMap copy];
// clear table and map
[_objectInfosMap removeAllObjects];
// unlock
pthread_mutex_unlock(&_lock);
_FBKVOSharedController *shareController = [_FBKVOSharedController sharedController];
for (id object in objectInfoMaps) {
// unobserve each registered object and infos
NSSet *infos = [objectInfoMaps objectForKey:object];
[shareController unobserve:object infos:infos];
}
}
@end