注册

自定义KVO(一)

实现一个简单的kvo

@interface NSObject (HP_KVO)

- (void)hp_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
- (void)hp_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context;
- (void)hp_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

@end

1.1 hp_addObserver

1.1.1参数检查

- (void)hp_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context {
//1.参数判断 以及 setter检查
if (!observer || !keyPath) return;
BOOL result = [self handleSetterMethodFromKeyPath:keyPath];
if (!result) return;
}

- (BOOL)handleSetterMethodFromKeyPath:(NSString *)keyPath {
SEL setterSeletor = NSSelectorFromString(setterForGetter(keyPath));
Method setterMethod = class_getInstanceMethod(object_getClass(self), setterSeletor);
NSAssert(setterMethod, @"%@ setter is not exist",keyPath);
return setterMethod ? YES : NO;
}

// 从get方法获取set方法的名称 key -> setKey
static NSString *setterForGetter(NSString *getter) {
if (getter.length <= 0) { return nil;}
NSString *firstString = [[getter substringToIndex:1] uppercaseString];
NSString *otherString = [getter substringFromIndex:1];
return [NSString stringWithFormat:@"set%@%@:",firstString,otherString];
}

由于只有属性才有效,所以先进行容错处理。

1.1.2 isa_swizzle动态生成子类

static NSString *const kHPKVOClassPrefix = @"HPKVONotifying_";

//申请类-注册类-添加方法
- (Class)creatKVOClassWithKeyPath:(NSString *)keyPath {
//这里重写class后kvo子类也返回的是父类的名字
NSString *superClassName = NSStringFromClass([self class]);
NSString *newClassName = [NSString stringWithFormat:@"%@%@",kHPKVOClassPrefix,superClassName];
Class newClass = NSClassFromString(newClassName);
//类是否存在
if (!newClass) {//不存在需要创建类
//1:申请类 父类、新类名称、额外空间
newClass = objc_allocateClassPair([self class], newClassName.UTF8String, 0);
//2:注册类
objc_registerClassPair(newClass);
//3:添加class方法,class返回父类信息 这里是`-class`
SEL classSEL = NSSelectorFromString(@"class");
Method classMethod = class_getInstanceMethod([self class], classSEL);
const char *classTypes = method_getTypeEncoding(classMethod);
class_addMethod(newClass, classSEL, (IMP)hp_class, classTypes);
}
//4:添加setter方法
SEL setterSEL = NSSelectorFromString(setterForGetter(keyPath));
Method setterMethod = class_getInstanceMethod([self class], setterSEL);
const char *setterTypes = method_getTypeEncoding(setterMethod);
class_addMethod(newClass, setterSEL, (IMP)hp_setter, setterTypes);

return newClass;
}

//返回父类信息
Class hp_class(id self,SEL _cmd){
return class_getSuperclass(object_getClass(self));
}

static void hp_setter(id self,SEL _cmd,id newValue){

}
  • 根据类名字拼接kvo类名字,判断是否已经存在。(superClassName由于class重写了,即使二次进入也获取到的是父类的名字)。
  • newClass不存在则调用objc_allocateClassPair创建kvo子类。并且重写- class方法。
  • 添加对应的setter方法。

当然也可以写+class,写入元类中。在objc_allocateClassPair后元类就存在了:


317dbbca399258f0cb538bcaa2872826.png

1.1.3 isa 指向子类

object_setClass(self, newClass);
  • 直接调用object_setClass设置objisa为新创建的kvo子类。

object_setClass源码:


Class object_setClass(id obj, Class cls)
{
if (!obj) return nil;
if (!cls->isFuture() && !cls->isInitialized()) {
lookUpImpOrNilTryCache(nil, @selector(initialize), cls, LOOKUP_INITIALIZE);
}

return obj->changeIsa(cls);
}

源码中就是修改对象的isa指向。


1.1.4 setter逻辑

在进行了上面逻辑的处理后,这个时候调用如下代码:

[self.obj hp_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
self.obj.name = @"HP";

会进入hp_setter函数。目前从HPObjectsetterName替换到了HPKVONotifying_ HPObjecthp_setter函数中。

hp_setter主要逻辑分两部分:调用父类方法以及发送通知

static void hp_setter(id self,SEL _cmd,id newValue) {
//自动开关判断,省略
//1.调用父类的setter(也可以通过performSelector调用)
void (*hp_msgSendSuper)(void *,SEL , id) = (void *)objc_msgSendSuper;
struct objc_super super_struct = {
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self)),
};
hp_msgSendSuper(&super_struct,_cmd,newValue);

//2.通知观察者
[observer hp_observeValueForKeyPath:getterForSetter(_cmd) ofObject:self change:@{} context:NULL];
}
  • 调用父类方法可以通过objc_msgSendSuper实现。
  • 通知观察者keypath可以通过_cmd转换获取,objectselfchange也可以获取到,context可以先不传。那么核心就是observer的获取。

通知观察者
首先想到的是用属性存储observer,那么有个问题在类已经创建后就无法添加了。所以关联属性明显更合适。在hp_addObserver中添加关联对象:

static NSString *const kHPKVOAssiociateKey = @"HPKVOAssiociateKey";

objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kHPKVOAssiociateKey), observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

通知逻辑实现:

//2.通知观察者
id observer = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kHPKVOAssiociateKey));
[observer hp_observeValueForKeyPath:getterForSetter(NSStringFromSelector(_cmd)) ofObject:self change:@{@"kind":@1,@"new":newValue} context:NULL];

//获取getter
static NSString *getterForSetter(NSString *setter){
if (setter.length <= 0 || ![setter hasPrefix:@"set"] || ![setter hasSuffix:@":"]) { return nil;}
NSRange range = NSMakeRange(3, setter.length - 4);
NSString *getter = [setter substringWithRange:range];
NSString *firstString = [[getter substringToIndex:1] lowercaseString];
return [getter stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:firstString];
}

这个时候在hp_observeValueForKeyPath中就有回调了:

change:{
kind = 1;
new = HP;
}

1.1.5 观察者信息保存

上面的逻辑虽然简单实现了,但是存在一个严重问题,观察多个属性的时候以及新旧值都要观察以及传递了context的情况就无效了。
那么就需要保存观察者相关的信息,创建一个新类HPKVOInfo实现如下:

typedef NS_OPTIONS(NSUInteger, HPKeyValueObservingOptions) {
HPKeyValueObservingOptionNew = 0x01,
HPKeyValueObservingOptionOld = 0x02,
};

@interface HPKVOInfo : NSObject

@property (nonatomic, weak) id observer;
@property (nonatomic, copy) NSString *keyPath;
@property (nonatomic, assign) HPKeyValueObservingOptions options;
@property (nonatomic, strong) id context;

- (instancetype)initWitObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(HPKeyValueObservingOptions)options context:(nullable void *)context;

@end

@implementation HPKVOInfo

- (instancetype)initWitObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(HPKeyValueObservingOptions)options context:(nullable void *)context {
self = [super init];
if (self) {
self.observer = observer;
self.keyPath = keyPath;
self.options = options;
self.context = (__bridge id _Nonnull)(context);
}
return self;
}

@end

hp_addObserver中信息保存修改如下:

//保存观察者信息-数组
HPKVOInfo *kvoInfo = [[HPKVOInfo alloc] initWitObserver:observer forKeyPath:keyPath options:options context:context];
NSMutableArray *observerArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kHPKVOAssiociateKey));
if (!observerArray) {
observerArray = [NSMutableArray arrayWithCapacity:1];
}
[observerArray addObject:kvoInfo];
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kHPKVOAssiociateKey), observerArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

hp_setter逻辑修改如下:

static void hp_setter(id self,SEL _cmd,id newValue) {
//自动开关判断,省略
//保存旧值
NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd));
id oldValue = [self valueForKey:keyPath];
//1.调用父类的setter(也可以通过performSelector调用)
void (*hp_msgSendSuper)(void *,SEL , id) = (void *)objc_msgSendSuper;
struct objc_super super_struct = {
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self)),
};
hp_msgSendSuper(&super_struct,_cmd,newValue);

//2.通知观察者
NSMutableArray *observerArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kHPKVOAssiociateKey));
for (HPKVOInfo *info in observerArray) {//循环调用,可能添加多次。
if ([info.keyPath isEqualToString:keyPath]) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSMutableDictionary<NSKeyValueChangeKey,id> *change = [NSMutableDictionary dictionaryWithCapacity:1];
//对新旧值进行处理
if (info.options & HPKeyValueObservingOptionNew) {
[change setObject:newValue forKey:NSKeyValueChangeNewKey];
}
if (info.options & HPKeyValueObservingOptionOld) {
if (oldValue) {
[change setObject:oldValue forKey:NSKeyValueChangeOldKey];
} else {
[change setObject:@"" forKey:NSKeyValueChangeOldKey];
}
}
[change setObject:@1 forKey:@"kind"];
//消息发送给观察者
[info.observer hp_observeValueForKeyPath:keyPath ofObject:self change:change context:(__bridge void * _Nullable)(info.context)];
});
}
}
}
  • 在调用父类之前先获取旧值。
  • 取出关联对象数组数据,循环判断调用hp_observeValueForKeyPath通知观察者。

这个时候观察多个属性以及多次观察就都没问题了。

1.2 hp_removeObserver

观察者对象是保存在关联对象中,所以在移除的时候也需要删除关联对象,并且当没有观察者时就要回复isa指向了。

- (void)hp_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
[self hp_removeObserver:observer forKeyPath:keyPath context:NULL];
}

- (void)hp_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context {
NSMutableArray *observerArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kHPKVOAssiociateKey));
if (observerArray.count <= 0) {
return;
}

NSMutableArray *tempArray = [observerArray mutableCopy];
for (HPKVOInfo *info in tempArray) {
if ([info.keyPath isEqualToString:keyPath]) {
if (info.observer) {
if (info.observer == observer) {
if (context != NULL) {
if (info.context == context) {
[observerArray removeObject:info];
}
} else {
[observerArray removeObject:info];
}
}
} else {
if (context != NULL) {
if (info.context == context) {
[observerArray removeObject:info];
}
} else {
[observerArray removeObject:info];
}
}
}
}
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kHPKVOAssiociateKey), observerArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//已经全部移除了
if (observerArray.count <= 0) {
//isa指回给父类
Class superClass = [self class];
object_setClass(self, superClass);
}
}
  • 通过keyPath以及observercontext确定要移除的关联对象数据。
  • 当关联对象中没有数据的时候isa进行指回。

完整代码如下:

@interface NSObject (HP_KVO)

- (void)hp_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(HPKeyValueObservingOptions)options context:(nullable void *)context;
- (void)hp_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context;
- (void)hp_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

- (void)hp_observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;

@end

#import "NSObject+HP_KVO.h"
#import
#import

static NSString *const kHPKVOClassPrefix = @"HPKVONotifying_";
static NSString *const kHPKVOAssiociateKey = @"HPKVOAssiociateKey";

@implementation NSObject (HP_KVO)

- (void)hp_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(HPKeyValueObservingOptions)options context:(nullable void *)context {
//1.参数判断 以及 setter检查
if (!observer || !keyPath) return;
BOOL result = [self handleSetterMethodFromKeyPath:keyPath];
if (!result) return;

//2.isa_swizzle 申请类-注册类-添加方法
Class newClass = [self creatKVOClassWithKeyPath:keyPath];

//3.isa 指向子类
object_setClass(self, newClass);
//4.setter逻辑处理
//保存观察者信息-数组
HPKVOInfo *kvoInfo = [[HPKVOInfo alloc] initWitObserver:observer forKeyPath:keyPath options:options context:context];
NSMutableArray *observerArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kHPKVOAssiociateKey));
if (!observerArray) {
observerArray = [NSMutableArray arrayWithCapacity:1];
}
[observerArray addObject:kvoInfo];
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kHPKVOAssiociateKey), observerArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void)hp_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
[self hp_removeObserver:observer forKeyPath:keyPath context:NULL];
}

- (void)hp_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context {
NSMutableArray *observerArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kHPKVOAssiociateKey));
if (observerArray.count <= 0) {
return;
}

NSMutableArray *tempArray = [observerArray mutableCopy];
for (HPKVOInfo *info in tempArray) {
if ([info.keyPath isEqualToString:keyPath]) {
if (info.observer) {
if (info.observer == observer) {
if (context != NULL) {
if (info.context == context) {
[observerArray removeObject:info];
}
} else {
[observerArray removeObject:info];
}
}
} else {
if (context != NULL) {
if (info.context == context) {
[observerArray removeObject:info];
}
} else {
[observerArray removeObject:info];
}
}
}
}
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kHPKVOAssiociateKey), observerArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//已经全部移除了
if (observerArray.count <= 0) {
//isa指回给父类
Class superClass = [self class];
object_setClass(self, superClass);
}
}

- (BOOL)handleSetterMethodFromKeyPath:(NSString *)keyPath {
SEL setterSeletor = NSSelectorFromString(setterForGetter(keyPath));
Method setterMethod = class_getInstanceMethod(object_getClass(self), setterSeletor);
NSAssert(setterMethod, @"%@ setter is not exist",keyPath);
return setterMethod ? YES : NO;
}

// 从get方法获取set方法的名称 key -> setKey
static NSString *setterForGetter(NSString *getter) {
if (getter.length <= 0) { return nil;}
NSString *firstString = [[getter substringToIndex:1] uppercaseString];
NSString *otherString = [getter substringFromIndex:1];
return [NSString stringWithFormat:@"set%@%@:",firstString,otherString];
}

//申请类-注册类-添加方法
- (Class)creatKVOClassWithKeyPath:(NSString *)keyPath {
//这里重写class后kvo子类也返回的是父类的名字
NSString *superClassName = NSStringFromClass([self class]);
NSString *newClassName = [NSString stringWithFormat:@"%@%@",kHPKVOClassPrefix,superClassName];
Class newClass = NSClassFromString(newClassName);
//类是否存在
if (!newClass) {//不存在需要创建类
//1:申请类 父类、新类名称、额外空间
newClass = objc_allocateClassPair([self class], newClassName.UTF8String, 0);
//2:注册类
objc_registerClassPair(newClass);
//3:添加class方法,class返回父类信息 这里是`-class`
SEL classSEL = NSSelectorFromString(@"class");
Method classMethod = class_getInstanceMethod([self class], classSEL);
const char *classTypes = method_getTypeEncoding(classMethod);
class_addMethod(newClass, classSEL, (IMP)hp_class, classTypes);
}
//4:添加setter方法
SEL setterSEL = NSSelectorFromString(setterForGetter(keyPath));
Method setterMethod = class_getInstanceMethod([self class], setterSEL);
const char *setterTypes = method_getTypeEncoding(setterMethod);
class_addMethod(newClass, setterSEL, (IMP)hp_setter, setterTypes);

return newClass;
}

//返回父类信息
Class hp_class(id self,SEL _cmd) {
return class_getSuperclass(object_getClass(self));
}

static void hp_setter(id self,SEL _cmd,id newValue) {
//自动开关判断,省略
//保存旧值
NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd));
id oldValue = [self valueForKey:keyPath];
//1.调用父类的setter(也可以通过performSelector调用)
void (*hp_msgSendSuper)(void *,SEL , id) = (void *)objc_msgSendSuper;
struct objc_super super_struct = {
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self)),
};
hp_msgSendSuper(&super_struct,_cmd,newValue);

//2.通知观察者
NSMutableArray *observerArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kHPKVOAssiociateKey));
for (HPKVOInfo *info in observerArray) {//循环调用,可能添加多次。
if ([info.keyPath isEqualToString:keyPath]) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSMutableDictionary<NSKeyValueChangeKey,id> *change = [NSMutableDictionary dictionaryWithCapacity:1];
//对新旧值进行处理
if (info.options & HPKeyValueObservingOptionNew) {
[change setObject:newValue forKey:NSKeyValueChangeNewKey];
}
if (info.options & HPKeyValueObservingOptionOld) {
if (oldValue) {
[change setObject:oldValue forKey:NSKeyValueChangeOldKey];
} else {
[change setObject:@"" forKey:NSKeyValueChangeOldKey];
}
}
[change setObject:@1 forKey:@"kind"];
//消息发送给观察者
[info.observer hp_observeValueForKeyPath:keyPath ofObject:self change:change context:(__bridge void * _Nullable)(info.context)];
});
}
}
}

//获取getter
static NSString *getterForSetter(NSString *setter){
if (setter.length <= 0 || ![setter hasPrefix:@"set"] || ![setter hasSuffix:@":"]) { return nil;}
NSRange range = NSMakeRange(3, setter.length - 4);
NSString *getter = [setter substringWithRange:range];
NSString *firstString = [[getter substringToIndex:1] lowercaseString];
return [getter stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:firstString];
}

@end

二、kvo函数式编程

上面的自定义自定义kvo与系统的kvo实现都有一个问题,都需要三步曲。代码是分离的可读性并不好。

2.1 注册与回调绑定

可以定义一个block用来处理回调,这样就不需要回调方法了,注册和回调就可以在一起处理了。
直接修改注册方法为block实现:

typedef void(^HPKVOBlock)(id observer,NSString *keyPath,id oldValue,id newValue);

- (void)hp_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath block:(HPKVOBlock)block {
……
//保存观察者信息-数组
HPKVOBlockInfo *kvoInfo = [[HPKVOBlockInfo alloc] initWitObserver:observer forKeyPath:keyPath handleBlock:block];
……
}
  • block实现也保存在HPKVOBlockInfo中,这样在回调的时候直接执行block实现就可以了。

修改回调逻辑:

NSMutableArray *observerArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kHPBlockKVOAssiociateKey));
for (HPKVOBlockInfo *info in observerArray) {//循环调用,可能添加多次。
if ([info.keyPath isEqualToString:keyPath] && info.handleBlock) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
info.handleBlock(info.observer, keyPath, oldValue, newValue);
});
}
}

  • 在回调的时候直接将新值与旧值一起返回。

注册调用逻辑:

[self.obj hp_addObserver:self forKeyPath:@"name" block:^(id  _Nonnull observer, NSString * _Nonnull keyPath, id  _Nonnull oldValue, id  _Nonnull newValue) {
NSLog(@"block: oldValue:%@,newValue:%@",oldValue,newValue);
}];

这样就替换了回调函数为block实现了,注册和回调逻辑在一起了。

2.2 kvo自动销毁

上面虽然实现了注册和回调绑定,但是在观察者dealloc的时候仍然需要remove
那么怎么能自动释放不需要主动调用呢?

removeObserver的过程中主要做了两件事,移除关联对象数组中的数据以及指回isa。关联对象不移除的后果是会继续调用回调,那么在调用的时候判断下observer存不存在来处理是否回调就可以了。核心就在指回isa了。

2.2.1 Hook dealloc

首先想到的就是Hook dealloc方法:

+ (void)hp_methodSwizzleWithClass:(Class)cls oriSEL:(SEL)oriSEL swizzledSEL:(SEL)swizzledSEL isClassMethod:(BOOL)isClassMethod {
if (!cls) {
NSLog(@"class is nil");
return;
}
if (!swizzledSEL) {
NSLog(@"swizzledSEL is nil");
return;
}
//类/元类
Class swizzleClass = isClassMethod ? object_getClass(cls) : cls;
Method oriMethod = class_getInstanceMethod(swizzleClass, oriSEL);
Method swiMethod = class_getInstanceMethod(swizzleClass, swizzledSEL);
if (!oriMethod) {//原始方法没有实现
// 在oriMethod为nil时,替换后将swizzledSEL复制一个空实现
class_addMethod(swizzleClass, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
//添加一个空的实现
method_setImplementation(swiMethod, imp_implementationWithBlock(^(id self, SEL _cmd){
NSLog(@"imp default null implementation");
}));
}
//自己没有则会添加成功,自己有添加失败
BOOL success = class_addMethod(swizzleClass, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(oriMethod));
if (success) {//自己没有方法添加一个,添加成功则证明自己没有。
class_replaceMethod(swizzleClass, swizzledSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
} else { //自己有直接进行交换
method_exchangeImplementations(oriMethod, swiMethod);
}
}

+ (void)load {
[self hp_methodSwizzleWithClass:[self class] oriSEL:NSSelectorFromString(@"dealloc") swizzledSEL:@selector(hp_dealloc) isClassMethod:NO];
}

- (void)hp_dealloc {
// [self.obj hp_removeObserver:self forKeyPath:@""];
[self hp_dealloc];
}
hp_dealloc中调用hp_removeObserver移除观察者。这里有个问题是被观察者和keypath从哪里来?这里相当于是观察者的dealloc中调用。所以可以通过在注册的时候对观察者添加关联对象保存被观察者和keyPath

static NSString *const kHPBlockKVOObserverdAssiociateKey = @"HPKVOObserverdAssiociateKey";

@interface HPKVOObservedInfo : NSObject

@property (nonatomic, weak) id observerd;
@property (nonatomic, copy) NSString *keyPath;

@end

@implementation HPKVOObservedInfo

- (instancetype)initWitObserverd:(NSObject *)observerd forKeyPath:(NSString *)keyPath {
if (self=[super init]) {
_observerd = observerd;
_keyPath = keyPath;
}
return self;
}

@end


- (void)hp_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath block:(HPKVOBlock)block {
……
//保存被观察者信息
HPKVOObservedInfo *kvoObservedInfo = [[HPKVOObservedInfo alloc] initWitObserverd:self forKeyPath:keyPath];
NSMutableArray *observerdArray = objc_getAssociatedObject(observer, (__bridge const void * _Nonnull)(kHPBlockKVOObserverdAssiociateKey));
if (!observerdArray) {
observerdArray = [NSMutableArray arrayWithCapacity:1];
}
[observerdArray addObject:kvoObservedInfo];
objc_setAssociatedObject(observer, (__bridge const void * _Nonnull)(kHPBlockKVOObserverdAssiociateKey), observerdArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

  • kvoObservedInfo中保存的是self也就是被观察者。
  • 关联对象关联在observer也就是观察者身上。

这个时候在dealloc中遍历对其进行移除:

- (void)hp_dealloc {
NSMutableArray *observerdArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kHPBlockKVOObserverdAssiociateKey));
for (HPKVOObservedInfo *info in observerdArray) {
if (info.observerd) {
[info.observerd hp_removeObserver:self forKeyPath:info.keyPath];
}
}
[self hp_dealloc];
}

当然这里的方法执行只针对被观察者没有释放的情况,释放了observerd就不存在了不需要调用remove逻辑了。

篇幅有限 下片继续



作者:HotPotCat
链接:https://www.jianshu.com/p/a57d0d98cc21








0 个评论

要回复文章请先登录注册