1、了解KVO
@interface ViewController ()
@property (nonatomic,strong)Person *p1;
@property (nonatomic,strong)Person *p2;
@end
@implementation ViewController
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"变了---key:%@ ---change:%@",keyPath,change);
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_p1 = [[Person alloc]init];
_p2 = [[Person alloc]init];
_p1.personName = @"lily";
_p2.personName = @"lucy";
NSLog(@"监听之前---p1:%p ---p2:%p",[_p1 methodForSelector:@selector(setPersonName:)],[_p2 methodForSelector:@selector(setPersonName:)]);
[_p1 addObserver:self forKeyPath:@"personName" options:NSKeyValueObservingOptionNew context:nil];
_p1.personName = @"macBookPro";
/*
(lldb) po _p1
<Person: 0x6000033b43f0>
(lldb) po object_getClassName(_p1)
"NSKVONotifying_Person"
*/
NSLog(@"监听之后---p1:%p ---p2:%p",[_p1 methodForSelector:@selector(setPersonName:)],[_p2 methodForSelector:@selector(setPersonName:)]);
}
打印结果:
2021-05-22 15:54:52.280855+0800 hahahah[20817:1289137] 监听之前---p1:0x104136c90 ---p2:0x104136c90
2021-05-22 15:54:52.281967+0800 hahahah[20817:1289137] 变了---key:personName ---change:{
kind = 1;
new = macBookPro;
}
2021-05-22 15:54:52.282090+0800 hahahah[20817:1289137] 监听之后---p1:0x7fff207bbb57 ---p2:0x104136c90
2、自定义实现KVO
.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic,copy)NSString *personName;
@end
NS_ASSUME_NONNULL_END
.m
#import "Person.h"
#import <objc/message.h>
@implementation Person
void setterMethed(id self,SEL _cmd,NSString *key){
//1、重写父类方法
struct objc_super superClass = {
self,
class_getSuperclass([self class])
};
objc_msgSendSuper(&superClass,_cmd,key);
//2、通知观察者,执行回调方法
id observer = objc_getAssociatedObject(self, (__bridge const void *)@"myObjc");
//key--
NSString *methedName = NSStringFromSelector(_cmd);
NSString *myKey = getMethedName(methedName);
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject: change:context:),myKey,self,@{myKey:key},nil);
}
NSString * getMethedName(NSString *setter){
NSRange range = NSMakeRange(3, setter.length-4);
NSString *key = [setter substringWithRange:range];
NSString *letter = [[key substringToIndex:1]lowercaseString];
key = [key stringByReplacingCharactersInRange:(NSMakeRange(0, 1)) withString:letter];
return key;
}
- (void)hh_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
NSString *oldClassStr = NSStringFromClass([self class]);
NSString *newClassStr = [NSString stringWithFormat:@"hhKVO_%@",oldClassStr];
//1、初始化类对
Class newClass = objc_allocateClassPair([self class], newClassStr.UTF8String, 0);
//2、注册类对
objc_registerClassPair(newClass);
//3、修改isa
object_setClass(self, newClass);
//4、重写set方法
NSString *newSetMethed = [NSString stringWithFormat:@"set%@",keyPath.capitalizedString];
SEL sel = NSSelectorFromString(newSetMethed);
class_addMethod(newClass, sel, (IMP)setterMethed, "v@:@");
objc_setAssociatedObject(self, (__bridge const void *)@"myObjc", observer, OBJC_ASSOCIATION_ASSIGN);
}
@end
网友评论