“这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战”
一.前言
KVO
的一些细节- 探索
KVO
原理 - 自定义
KVO
一.KVO细节分析上
1.关于context
的细节 官方说明如下图
注册
监听回调
删除
static void *PersonNameContext = &PersonNameContext;
@interface LGViewController ()
@property (nonatomic, strong) LGPerson *person;
@end
@implementation LGViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [LGPerson new];
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:PersonNameContext];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.person.name = @"test";
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if(context == PersonNameContext){
NSLog(@"%@",change);
}
}
- (void)dealloc{
[self.person removeObserver:self forKeyPath:@"name" context:PersonNameContext];
}
复制代码
打印结果
根据官方文档 如果我们用KVO
没removeObserver
的话 例如:进入一个VC
里面有一个单例持有观察VC
的属性 哪怕VC
被释放 单例没释放继续观察 在次进入VC
的时候 观察属性发生变化 之前已经被释放的VC
会继续接收到消息 因为已经被释放 所以野指针 崩溃了
二.KVO细节分析下
1.手动观察属性
// 自动开关
+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key{
return NO;
}
- (void)setName:(NSString *)name
{
[self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"];
}
复制代码
2.监听一个属性 通过另外两个变量去控制
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:@"downloadProgress"]) {
NSArray *affectingKeys = @[@"totalData", @"writtenData"];
keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
}
return keyPaths;
}
- (NSString *)downloadProgress{
if (self.writtenData == 0) {
self.writtenData = 10;
}
if (self.totalData == 0) {
self.totalData = 100;
}
return [[NSString alloc] initWithFormat:@"%f",1.0f*self.writtenData/self.totalData];
}
//监听downloadProgress这个属性
[self.person addObserver:self forKeyPath:@"downloadProgress" options:(NSKeyValueObservingOptionNew) context:NULL];
//改变这两个属性
self.person.writtenData += 10;
self.person.totalData += 1;
复制代码
3.对可变数组的观察
[self.person addObserver:self forKeyPath:@"dateArray" options:(NSKeyValueObservingOptionNew) context:PersonDataArrayContext];
self.person.dateArray = [NSMutableArray arrayWithCapacity:1];
[[self.person mutableArrayValueForKey:@"dateArray"] addObject:@"1"];
打印结果
2021-08-15 11:16:20.182185+0800 001---KVO初探[39513:1527764] {
kind = 1;
new = (
);
}
2021-08-15 11:16:25.016788+0800 001---KVO初探[39513:1527764] {
indexes = "<_NSCachedIndexSet: 0x6000010b84e0>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
kind = 2;
new = (
1
);
}
typedef NS_ENUM(NSUInteger, NSKeyValueChange) {
NSKeyValueChangeSetting = 1,
NSKeyValueChangeInsertion = 2,
NSKeyValueChangeRemoval = 3,
NSKeyValueChangeReplacement = 4,
};
kind=2的类型是insert
复制代码
三.KVO原理上
1.kvo
原理分析
- 只对属性观察
setter
- 中间类 –
self.person
->LGPerson isa
发生了变化NSKVONotifying_LGPerson (LGPerson 子类)
- 有什么东西 – 方法 – 属性
setNickName
–class
–dealloc
–_isKVOA
- 继承 – 重写 – 实实在在的 实现
setter 子类
– 父类改变nickName
传值willchange
父类的setter
didChange
NSKVONotifying_LGPerson
是否移除+isa
是否会回来 在移除观察的时候
//添加监听的时候确实动态生成了NSKVONotifying_LGPerson
#pragma mark - 遍历类以及子类
- (void)printClasses:(Class)cls{
// 注册类的总数
int count = objc_getClassList(NULL, 0);
// 创建一个数组, 其中包含给定对象
NSMutableArray *mArray = [NSMutableArray arrayWithObject:cls];
// 获取所有已注册的类
Class* classes = (Class*)malloc(sizeof(Class)*count);
objc_getClassList(classes, count);
for (int i = 0; i<count; i++) {
if (cls == class_getSuperclass(classes[i])) {
[mArray addObject:classes[i]];
}
}
free(classes);
NSLog(@"classes = %@", mArray);
}
复制代码
四.KVO原理下
- 1.
isa
->LGPerson
->NSKVONotifying_LGPerson
–消失
- 1.1 动态生成
NSKVONotifying_LGPerson
- 1.2
LGPerson
VSNSKVONotifying_LGPerson
父子 - 1.3
NSKVONotifying_LGPerson
有哪些方法 -
`setNickName` //重写set方法 复制代码
-
`class` 复制代码
-
`dealloc` 复制代码
-
`_isKVOA` 复制代码
- 1.4
isa
指回来 //通过这个方法_isKVOA
- 1.5
NSKVONotifying_LGPerson
是否销毁
-2.setter
方KVO
实例法(setter)/class
-
2.1 成员 VS 属性
-
2.2 修改
LGPerson
属性
#pragma mark - 遍历方法-ivar-property
- (void)printClassAllMethod:(Class)cls{
unsigned int count = 0;
Method *methodList = class_copyMethodList(cls, &count);
for (int i = 0; i<count; i++) {
Method method = methodList[i];
SEL sel = method_getName(method);
IMP imp = class_getMethodImplementation(cls, sel);
NSLog(@"%@-%p",NSStringFromSelector(sel),imp);
}
free(methodList);
}
//遍历NSKVONotifying_LGPerson有哪些方法
[self printClassAllMethod:objc_getClass("NSKVONotifying_LGPerson")];
2021-08-15 12:20:25.681840+0800 002---KVO原理探讨[39748:1566211] setNickName:-0x7fff207bf03f
2021-08-15 12:20:25.682122+0800 002---KVO原理探讨[39748:1566211] class-0x7fff207bdb49
2021-08-15 12:20:25.682305+0800 002---KVO原理探讨[39748:1566211] dealloc-0x7fff207bd8f7
2021-08-15 12:20:25.682472+0800 002---KVO原理探讨[39748:1566211] _isKVOA-0x7fff207bd8ef
复制代码
remove
前是NSKVONotifying_LGPerson
remove
后是LGPerson
说明isa
指回来了
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END