本篇文章提纲:
1、自定义KVO
2、函数式KVO
3、KVO的自动销毁机制
4、FBKVOController
5、GNUStep Base
自定义KVO
我们仿照KVO的实现方式,自定义一个NSObecjt的一个分类LGKVO。
- 自定义添加观察者lg_addObserver
- (void)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(LGKeyValueObservingOptions)options context:(nullable void *)context{
// 1: 验证是否存在setter方法 : 不让实例进来
[self judgeSetterMethodFromKeyPath:keyPath];
// 2: 动态生成子类
Class newClass = [self createChildClassWithKeyPath:keyPath];
// 3: isa的指向 : LGKVONotifying_LGPerson
object_setClass(self, newClass);
// 4: 保存观察者信息
LGKVOInfo *info = [[LGKVOInfo alloc] initWitObserver:observer forKeyPath:keyPath options:options];
NSMutableArray *observerArr = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey));
if (!observerArr) {
observerArr = [NSMutableArray arrayWithCapacity:1];
[observerArr addObject:info];
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey), observerArr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
- (void)judgeSetterMethodFromKeyPath:(NSString *)keyPath{
Class superClass = object_getClass(self);
SEL setterSeletor = NSSelectorFromString(setterForGetter(keyPath));
Method setterMethod = class_getInstanceMethod(superClass, setterSeletor);
if (!setterMethod) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"老铁没有当前%@的setter",keyPath] userInfo:nil];
}
}
- (Class)createChildClassWithKeyPath:(NSString *)keyPath{
NSString *oldClassName = NSStringFromClass([self class]);
NSString *newClassName = [NSString stringWithFormat:@"%@%@",kLGKVOPrefix,oldClassName];
Class newClass = NSClassFromString(newClassName);
// 防止重复创建生成新类
if (newClass) return newClass;
/**
* 如果内存不存在,创建生成
* 参数一: 父类
* 参数二: 新类的名字
* 参数三: 新类的开辟的额外空间
*/
// 2.1 : 申请类
newClass = objc_allocateClassPair([self class], newClassName.UTF8String, 0);
// 2.2 : 注册类
objc_registerClassPair(newClass);
// 2.3.1 : 添加class : class的指向是LGPerson
SEL classSEL = NSSelectorFromString(@"class");
Method classMethod = class_getInstanceMethod([self class], classSEL);
const char *classTypes = method_getTypeEncoding(classMethod);
class_addMethod(newClass, classSEL, (IMP)lg_class, classTypes);
// 2.3.2 : 添加setter
SEL setterSEL = NSSelectorFromString(setterForGetter(keyPath));
Method setterMethod = class_getInstanceMethod([self class], setterSEL);
const char *setterTypes = method_getTypeEncoding(setterMethod);
class_addMethod(newClass, setterSEL, (IMP)lg_setter, setterTypes);
return newClass;
}
通过LGKVOInfo保存信息
- (instancetype)initWitObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(LGKeyValueObservingOptions)options{
self = [super init];
if (self) {
self.observer = observer;
self.keyPath = keyPath;
self.options = options;
}
return self;
}
解析:
首先,要确保被观察者有setter方法,否则添加观察无效,被观察的对象没有通过setter方法赋新值,也就观察不到变化。
其次,我们仿照这系统KVO动态生成一个新的子类。
然后,我们让原来的类的isa指向这个新生成的子类。
最后,保存观察者信息。
-
实现子类方法
我们自定义KVO动态添加方法的时候,isa指向了新添加的类lg_class还有lg_setter
lg_class
Class lg_class(id self,SEL _cmd){
return class_getSuperclass(object_getClass(self));
}
获取的class还是原始的类的对象。
lg_setter
static void lg_setter(id self,SEL _cmd,id newValue){
NSLog(@"来了:%@",newValue);
// 4: 消息转发 : 转发给父类
// 改变父类的值 --- 可以强制类型转换
NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd));
id oldValue = [self valueForKey:keyPath];
void (*lg_msgSendSuper)(void *,SEL , id) = (void *)objc_msgSendSuper;
// void /* struct objc_super *super, SEL op, ... */
struct objc_super superStruct = {
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self)),
};
//objc_msgSendSuper(&superStruct,_cmd,newValue)
lg_msgSendSuper(&superStruct,_cmd,newValue);
// 1: 拿到观察者
NSMutableArray *observerArr = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey));
for (LGKVOInfo *info in observerArr) {
if ([info.keyPath isEqualToString:keyPath]) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSMutableDictionary<NSKeyValueChangeKey,id> *change = [NSMutableDictionary dictionaryWithCapacity:1];
// 对新旧值进行处理
if (info.options & LGKeyValueObservingOptionNew) {
[change setObject:newValue forKey:NSKeyValueChangeNewKey];
}
if (info.options & LGKeyValueObservingOptionOld) {
[change setObject:@"" forKey:NSKeyValueChangeOldKey];
if (oldValue) {
[change setObject:oldValue forKey:NSKeyValueChangeOldKey];
}
}
// 2: 消息发送给观察者
SEL observerSEL = @selector(lg_observeValueForKeyPath:ofObject:change:context:);
objc_msgSend(info.observer,observerSEL,keyPath,self,change,NULL);
});
}
}
}
解析:
1、先处理父类,改变父类的值,把消息发给父类。(因为我们通过前面的了解知道,KVO是私底下生成了一个被观察者的类的子类,所以父类就是被观察者的类)。
2、获取观察者,观察者之前被保存了。我们通过遍历来找到匹配的属性,进一步的去处理变化。
3、处理新旧值。
4、值改变后通知观察者。
- 移除观察者lg_removeObserver
- (void)lg_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath{
NSMutableArray *observerArr = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey));
if (observerArr.count<=0) {
return;
}
for (LGKVOInfo *info in observerArr) {
if ([info.keyPath isEqualToString:keyPath]) {
[observerArr removeObject:info];
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey), observerArr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
break;
}
}
if (observerArr.count<=0) {
// 指回给父类
Class superClass = [self class];
object_setClass(self, superClass);
}
}
解析:
1、匹配到观察的对象之前保存的信息,然后移除掉;
2、isa指针指回原始类对象。
至此,自定义KVO的添加,值变化,通知值变化,销毁整个过程就完毕。
函数式KVO
- 函数式编程介绍
编程范式指的是编写命令的方法。编程语言的思想正是建立在其编程范式之上。最常见的三种范式分别是面向对象程序设计、命令式程序设计和函数式程序设计。这三种思想体系并无优劣之分,通常我们都需要选择正确的工具来完成工作。
和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。
和过程化编程相比,函数式编程里函数的计算可随时调用。
简单说,"函数式编程"是一种"编程范式"(programming paradigm),也就是如何编写程序的方法论。
它属于"结构化编程"的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。
函数式编程关心数据的映射,命令式编程关心解决问题的步骤。
- 定义响应的回调
typedef void(^LGKVOBlock)(id observer,NSString *keyPath,id oldValue,id newValue);
-
注册函数式KVO addObserver
相当于在原来的自定义的addObserver方法里,把定义的block再传进来,进行信息的保存。
- (void)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath block:(LGKVOBlock)block {
……
// 4: 保存信息
LGInfo *info = [[LGInfo alloc] initWitObserver:observer forKeyPath:keyPath handleBlock:block];
……
}
- 函数式KVO setter
static void lg_setter(id self,SEL _cmd,id newValue){
······
// 5: 信息数据回调
NSMutableArray *mArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey));
for (LGInfo *info in mArray) {
if ([info.keyPath isEqualToString:keyPath] && info.handleBlock) {
info.handleBlock(info.observer, keyPath, oldValue, newValue);
}
}
}
在前面自定义KVO中,在setter方法中进行了新值和旧值的分别处理。现在的block方式的封装,是通过这个block把所有的相关信息都通过这个block返回了,这样就实现了在添加观察者的后面,直接去处理结果,代码更加集中。
同样,关于kvo信息保存的部分,再增加一个block的存储即可。
运行结果.png- 自定义KVO的自动销毁
我们自定义完毕KVO,但是在观察者dealloc的时候需要手动remove掉观察,我们来尝试做一下自动释放,不需要主动去调用释放。
在removeObserver的过程中主要做了两件事情,移除关联对象数组中的数据,以及isa指回。关联对象不进行移除的话是会继续被调用的,那么我们在调用的时候去判断下observer是否存在来处理下是否回调。
- dealloc方法重写处理
我们在想要统一处理dealloc方法时,就是VC有可能自己实现了dealloc或者,也有可能自己没有实现这两种情况。我们通过方法交换,把系统方法dealloc替换成自己的mydealloc,如果VC自己实现了dealloc直接交换就可以了,如果没有实现,那么我们为他添加一个dealloc方法,然后再进行交换。
具体实现:
(自己添加了一些注释,可能理解的不对,如有误,望指正)
+ (BOOL)kc_hookOrigInstanceMenthod:(SEL)oriSEL newInstanceMenthod:(SEL)swizzledSEL {
Class cls = self;
//原始方法
Method oriMethod = class_getInstanceMethod(cls, oriSEL);
//交换的方法
Method swiMethod = class_getInstanceMethod(cls, swizzledSEL);
//要交换的方法为空
if (!swiMethod) {
return NO;
}
//原始的dealloc为空 添加一个
if (!oriMethod) {
//添加的类名 下的orisel实现为swiMethod 直接让orisel 指向了swiMethod
class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
method_setImplementation(swiMethod, imp_implementationWithBlock(^(id self, SEL _cmd){ }));
}
BOOL didAddMethod = class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
//方法已经被添加了
if (didAddMethod) {
/*cls 想要修改的类
*swizzledSEL 你想要被替换的实现
*method_getImplementation(oriMethod) 由cls标识的类的名称标识的方法的新实现。
*method_getTypeEncoding(oriMethod) 描述方法参数类型的字符数组。由于函数必须至少包含两个参数self和_cmd,因此第二个和第三个字符必须为“@:”(第一个字符是返回类型)。
*/
//所以这个根据前面添加的过程 现在是用method_getImplementation(oriMethod)替换了swizzledSEL,其实swizzledSEL和method_getImplementation(oriMethod)是一样的,因为前面添加oriMethod的实现的时候 是把swiMethod的实现赋值了过去。
class_replaceMethod(cls, swizzledSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
}else{
//如果添加失败 那么说明原来就有这个方法 直接交换imp
method_exchangeImplementations(oriMethod, swiMethod);
}
return YES;
}
class_replaceMethod.png
-
class_replaceMethod和method_exchangeImplementations的区别
class_replaceMethod
只修改前面被替换的那个IMP,后面的IMP不变;
method_exchangeImplementations
相互交换IMP,两个方法的指向都变了。
如果我们在分类里通过load方法这么搞,会把所有的dealloc方法都替换掉,这样不合理,我们目的是处理KVO的自动销毁,而不影响到系统方法的正常使用,所以这个交换放到动态创建子类的时候比较合适,创建过子类实现dealloc方法交换。
- (Class)createChildClassWithKeyPath:(NSString *)keyPath{
NSString *oldClassName = NSStringFromClass([self class]);
NSString *newClassName = [NSString stringWithFormat:@"%@%@",kLGKVOPrefix,oldClassName];
Class newClass = NSClassFromString(newClassName);
// 防止重复创建生成新类
if (newClass) return newClass;
// 2.1 : 申请类
newClass = objc_allocateClassPair([self class], newClassName.UTF8String, 0);
// 2.2 : 注册类
objc_registerClassPair(newClass);
// 2.3.1 : 添加class : class的指向是LGPerson
SEL classSEL = NSSelectorFromString(@"class");
Method classMethod = class_getInstanceMethod([self class], classSEL);
const char *classTypes = method_getTypeEncoding(classMethod);
class_addMethod(newClass, classSEL, (IMP)lg_class, classTypes);
// 2.3.2 : 添加setter
SEL setterSEL = NSSelectorFromString(setterForGetter(keyPath));
Method setterMethod = class_getInstanceMethod([self class], setterSEL);
const char *setterTypes = method_getTypeEncoding(setterMethod);
class_addMethod(newClass, setterSEL, (IMP)lg_setter, setterTypes);
//替换掉dealloc方法
[self kc_hookOrigInstanceMenthod:NSSelectorFromString(@"dealloc") newInstanceMenthod:@selector(myDealloc)];
return newClass;
}
- (void)myDealloc{
NSMutableArray *observerdArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey));
for (LGInfo *info in observerdArray) {
if (info.observer) {
[info.observer lg_removeObserver:self forKeyPath:info.keyPath];
}
}
}
FBKVOController
上述自定义的KVO代码并不完善,通过实践来感受一些KVO使用的痛点,下面我们通过FBKVOController,由Facebook自定义的KVO,项目已经开源。
特点:
1、采用代理模式,对常用的KVO进行封装;
2、可以对model中的多个属性进行监听;
3、提供action和block两种方式回调,业务代码更加紧凑;
4、提供自动销毁机制,不用自行移除观察者。
- 部分源码解析
初始化:
- (instancetype)initWithObserver:(nullable id)observer retainObserved:(BOOL)retainObserved
{
self = [super init];
if (nil != self) {
//一般情况下 observer 会持有 FBKVOController 为了避免循环引用,此处的_observer是弱引用
_observer = observer;
//定义 NSMapTable key的内存管理策略,在默认情况,传入的参数 retainObserved = YES
NSPointerFunctionsOptions keyOptions = retainObserved ? NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPointerPersonality : NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality;
//创建 NSMapTable
_objectInfosMap = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality capacity:0];
//初始化互斥锁加锁
pthread_mutex_init(&_lock, NULL);
}
return self;
}
注册观察者:
- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(FBKVONotificationBlock)block
{
NSAssert(0 != keyPath.length && NULL != block, @"missing required parameters observe:%@ keyPath:%@ block:%p", object, keyPath, 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 observe和info的关联
[self _observe:object info:info];
}
observe和info的关联:
去判断info,没有的话去创建,有的话直接返回。
- (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 如果为空 创建info给对应的object
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);
//info的一些处理判断 更新观察状态
[[_FBKVOSharedController sharedController] observe:object info:info];
}
FBKVOController回调:
- (void)observeValueForKeyPath:(nullable NSString *)keyPath
ofObject:(nullable id)object
change:(nullable NSDictionary<NSString *, id> *)change
context:(nullable void *)context
{
NSAssert(context, @"missing context keyPath:%@ object:%@ change:%@", keyPath, object, change);
_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) {
// 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<NSString *, 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];
}
info->_block(observer, object, changeWithKeyPath);
} else if (info->_action) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[observer performSelector:info->_action withObject:change withObject:object];
#pragma clang diagnostic pop
} else {
[observer observeValueForKeyPath:keyPath ofObject:object change:change context:info->_context];
}
}
}
}
}
通过info找到相关的controller,然后经过非空判断,进行change的处理和回调。
FBKVOController 的销毁:
- (void)dealloc
{
[self unobserveAll];
pthread_mutex_destroy(&_lock);
}
- (void)unobserveAll
{
[self _unobserveAll];
}
- (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];
}
}
- (void)unobserve:(id)object infos:(nullable NSSet<_FBKVOInfo *> *)infos
{
if (0 == infos.count) {
return;
}
// unregister info
pthread_mutex_lock(&_mutex);
for (_FBKVOInfo *info in infos) {
[_infos removeObject:info];
}
pthread_mutex_unlock(&_mutex);
// remove observer
for (_FBKVOInfo *info in infos) {
if (info->_state == _FBKVOInfoStateObserving) {
[object removeObserver:self forKeyPath:info->_keyPath context:(void *)info];
}
info->_state = _FBKVOInfoStateNotObserving;
}
}
销毁的部分主要是加锁,然后去遍历_objectInfosMap
表中的信息,拿到infos,通过方法unobserve
去处理infos,再去遍历infos把里面的info信息都清空。
GNUStep Base
KVO
和KVC
的代码苹果没有开源,我们可以通过GNUstep去探索下他们的原理。
- 源码看KVO注册
阅读的时候添加了一些相关注释
- (void) addObserver: (NSObject*)anObserver
forKeyPath: (NSString*)aPath
options: (NSKeyValueObservingOptions)options
context: (void*)aContext
{
GSKVOInfo *info; //和我们自定义的KVO时候的思路相似,用一个info对象来存储注册过的需要观察的对象
GSKVOReplacement *r;
//r里面包括原类和被替换之后的类的信息,下面是GSKVOReplacement包括的内容
///Class original; /* The original class */
///Class replacement; /* The replacement class */
///NSMutableSet *keys; /* The observed setter keys */
NSKeyValueObservationForwarder *forwarder; //这个里面大概装了一些观察的属性的状态 新值旧值这些
NSRange dot;
setup(); //一些表的初始化
[kvoLock lock];
// Use the original class 给原类创建一个子类
r = replacementForClass([self class]);
/*
* Get the existing observation information, creating it (and changing
* the receiver to start key-value-observing by switching its class)
* if necessary.
*/
//info为空 初始化info
info = (GSKVOInfo*)[self observationInfo];
if (info == nil)
{
info = [[GSKVOInfo alloc] initWithInstance: self];
[self setObservationInfo: info];
object_setClass(self, [r replacement]);
}
/*
* Now add the observer.
*/
dot = [aPath rangeOfString:@"."];
//判断是不是keypath的方式进行的注册
if (dot.location != NSNotFound)
{
forwarder = [[NSKeyValueObservationForwarder alloc]
initWithKeyPath: aPath
ofObject: self
withTarget: anObserver
context: aContext];
[info addObserver: anObserver
forKeyPath: aPath
options: options
context: forwarder];
}
else
{
//不是keypath的方式
[r overrideSetterFor: aPath];
[info addObserver: anObserver
forKeyPath: aPath
options: options
context: aContext];
}
[kvoLock unlock];
}
- replacementForClass
static GSKVOReplacement *
replacementForClass(Class c)
{
GSKVOReplacement *r;
setup();
[kvoLock lock];
r = (GSKVOReplacement*)NSMapGet(classTable, (void*)c);
if (r == nil)
{
r = [[GSKVOReplacement alloc] initWithClass: c];
NSMapInsert(classTable, (void*)c, (void*)r);
}
[kvoLock unlock];
return r;
}
- (id) initWithClass: (Class)aClass
{
NSValue *template;
NSString *superName;
NSString *name;
if (nil == (self = [super init]))
{
return nil;
}
if ([aClass instanceMethodForSelector: @selector(takeValue:forKey:)]
!= [NSObject instanceMethodForSelector: @selector(takeValue:forKey:)])
{
NSLog(@"WARNING The class '%@' (or one of its superclasses) overrides"
@" the deprecated takeValue:forKey: method. Using KVO to observe"
@" this class may interfere with this method. Please change the"
@" class to override -setValue:forKey: instead.",
NSStringFromClass(aClass));
}
if ([aClass instanceMethodForSelector: @selector(takeValue:forKeyPath:)]
!= [NSObject instanceMethodForSelector: @selector(takeValue:forKeyPath:)])
{
NSLog(@"WARNING The class '%@' (or one of its superclasses) overrides"
@" the deprecated takeValue:forKeyPath: method. Using KVO to observe"
@" this class may interfere with this method. Please change the"
@" class to override -setValue:forKeyPath: instead.",
NSStringFromClass(aClass));
}
original = aClass;
/*
* Create subclass of the original, and override some methods
* with implementations from our abstract base class.
*/
superName = NSStringFromClass(original);
name = [@"GSKVO" stringByAppendingString: superName];
template = GSObjCMakeClass(name, superName, nil);
GSObjCAddClasses([NSArray arrayWithObject: template]);
replacement = NSClassFromString(name);
GSObjCAddClassBehavior(replacement, baseClass);
/* Create the set of setter methods overridden.
*/
keys = [NSMutableSet new];
return self;
}
-
通过部分注释,我们可以看到
initWithClass
方法为 原类创建了一个subclass,并且这个子类重载了一些基类的方法实现。 -
GSObjCMakeClass
内部通过方法objc_allocateClassPair
创建了新类,新类的父类是这个原类。 -
GSObjCAddClasses
通过方法objc_registerClassPair
注册类。 -
GSObjCAddClassBehavior
处理method -
源码看KVO的setter处理
标记了红色的部分是对set进行简单的处理,for里面有个判断是关于settet方法是否找到的,如果查到了相关的setter方法,keys会add这个key,如果没查到会走KVC的流程继续查,还未找到setter的方法报错。
image.png- setter
- (void) setter: (void*)val
{
NSString *key;
Class c = [self class];
void (*imp)(id,SEL,void*);
imp = (void (*)(id,SEL,void*))[c instanceMethodForSelector: _cmd];
key = newKey(_cmd);
//自动开关是否开启的判断,开启了走willChangeValueForKey ,setter赋值,didChangeValueForKey
if ([c automaticallyNotifiesObserversForKey: key] == YES)
{
// pre setting code here
[self willChangeValueForKey: key];
(*imp)(self, _cmd, val);
// post setting code here
[self didChangeValueForKey: key];
}
else
{
//未开启直接赋值不走willChangeValueForKey和didChangeValueForKey
(*imp)(self, _cmd, val);
}
RELEASE(key);
}
- willChangeValueForKey
- (void) willChangeValueForKey: (NSString*)aKey
{
GSKVOPathInfo *pathInfo;
GSKVOInfo *info;
info = (GSKVOInfo *)[self observationInfo];
if (info == nil)
{
return;
}
pathInfo = [info lockReturningPathInfoForKey: aKey];
if (pathInfo != nil)
{
if (pathInfo->recursion++ == 0)
{
//原来的新值 变成旧值
id old = [pathInfo->change objectForKey: NSKeyValueChangeNewKey];
// 旧值存在
if (old != nil)
{
/* We have set a value for this key already, so the value
* we set must now be the old value and we don't need to
* refetch it.
*/
//NSKeyValueChangeOldKey 下的旧值更新
[pathInfo->change setObject: old
forKey: NSKeyValueChangeOldKey];
//移除掉没set之前的新值
[pathInfo->change removeObjectForKey: NSKeyValueChangeNewKey];
}
else if (pathInfo->allOptions & NSKeyValueObservingOptionOld)
{
//原来没有值 那么旧值也是空的
/* We don't have an old value set, so we must fetch the
* existing value because at least one observation wants it.
*/
old = [self valueForKey: aKey];
if (old == nil)
{
old = null;
}
[pathInfo->change setObject: old
forKey: NSKeyValueChangeOldKey];
}
[pathInfo->change setValue:
[NSNumber numberWithInt: NSKeyValueChangeSetting]
forKey: NSKeyValueChangeKindKey];
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: YES];
}
[info unlock];
}
[self willChangeValueForDependentsOfKey: aKey];
}
以上添加了注释,简单的理解就是willChangeValueForKey
是对旧值进行处理,如果在这之前原来有旧值 那么更新下,此刻新值成为了旧值,旧值被替换为上一次值变化的新值,新值被remove掉。
如果之前都没有值就都是NULL。
- didChangeValueForKey
简述,此方法是去更新新值,并且通过方法notifyForKey
发送通知。
- 源码看KVO removeObserver
对相关的info进行移除,并且isa指回原类。
以上就是通过GNU step
了解到的KVO的一部分实现逻辑,里面还有大量的代码需要去学习,这看到的只是冰山一角,了解了一些大概,里面还有很详细对新旧值的各种处理和判断,还有setter方法对不同类型的值进行的处理等等,以后若时间充足可以更加深入和详细的研究。
网友评论