您的位置:首页 > 移动开发 > IOS开发

iOS KVO 常见错误

2015-11-11 14:22 489 查看
一、KVO

  Key-Value Observing 键值用于检测对象的某些属性的实时变化情况并作出响应

KVO实现

/** KVO 实现 */
self.womanPerson = [[WLPerson alloc] init];
[self.womanPerson addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
self.womanPerson.name = @"rose";

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
/** 同一个类里可能有多个KVO */
if (object == self.person && [keyPath isEqualToString:@"name"]) {
WLPerson *person = object;
NSLog(@"---->%@", person.name);
} else if (object == self.womanPerson && [keyPath isEqualToString:@"name"]) {
WLPerson *person = object;
NSLog(@"---->%@", person.name);
}
}


在为一个对象添加观察者之时,framework使用runtime动态创建了一个WLPerson类的子类NSKVONotifying_WLPerson,而为了不让外部知道这一行为,NSKVONotifying_WLPerson重写了-(void)class方法返回之前的类

self.person = [[WLPerson alloc] init];
NSLog(@"\n-添加监听者之前-.class-->%@  \n            ---isa--->%s", NSStringFromClass(self.person.class), object_getClassName(self.person));
object_getClass(self.person);

[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
NSLog(@"\n-添加监听者之后-.class-->%@  \n            ---isa--->%s", NSStringFromClass(self.person.class), object_getClassName(self.person));

[self.person removeObserver:self forKeyPath:@"name" context:nil];
NSLog(@"\n-移除监听者之后-.class-->%@  \n            ---isa--->%s", NSStringFromClass(self.person.class), object_getClassName(self.person));

打印:
-添加监听者之前-.class-->WLPerson
---isa--->WLPerson
-添加监听者之后-.class-->WLPerson
---isa--->NSKVONotifying_WLPerson
-移除监听者之后-.class-->WLPerson
---isa--->WLPerson


可以看到在添加观察之后.class打印出来的类没有变化,使用object_getClassName()打印出来的类是NSKVONotifying_WLPerson ,因为这个object_getClass()返回的是这个对象的isa指针,isa指针指向的一定是这个对象所属的类。

二、常见错误

1、romove 观察者

[self removeObserver:self forKeyPath:@"name" context:nil];
*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <ViewController 0x121d099d0> for the key path "name" from <ViewController 0x121d099d0> because it is not registered as an observer.'

原因:crash信息已经很清楚了,谁添加的观察者,谁移除


2、重复移除观察者

[self removeObserver:self forKeyPath:@"name" context:nil];
[self removeObserver:self forKeyPath:@"name" context:nil];

crash *** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <ViewController 0x100306e50> for the key path "name" from <WLPerson 0x17400a740> because it is not registered as an observer.’

解决:网上有很多方法,最简单的try catch
@try {
[self.person removeObserver:self forKeyPath:@"name" context:nil];
[self.person removeObserver:self forKeyPath:@"name" context:nil];
} @catch (NSException *exception) {
NSLog(@"重复移除");
} @finally {

}
可能有人会问了,谁这么傻逼移除两次,其实在很多情况下确实写的移除一次,但是可能。。。。。比如SDPhotobrowser就存在这样的crash


3、属性的值修改了的信息收到了,但是没有处理 方法-observeValueForKeyPath:ofObject:change:context:却没有实现,只要你注册了观察者,这个方法必须实现

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<ViewController: 0x10040a1e0>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: name
Observed object: <WLPerson: 0x170005d00>
Change: {
kind = 1;
new = jack;
}
Context: 0x0’


4、添加和移除时,content上下文不一致

[self.person removeObserver:self forKeyPath:@"name" context:@"随便给个"];
*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <ViewController 0x100704960> for the key path "name" from <WLPerson 0x1740046f0> because it is not registered as an observer.’

ps:一般传nil


5、给局部变量添加观察者

WLPerson *tempPerson = [[WLPerson alloc] init];
[tempPerson addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
tempPerson.name = @"jack”;
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x174000880 of class WLPerson was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x174022800> (
<NSKeyValueObservance 0x174045a00: Observer: 0x100307e70, Key path: name, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x174045be0>
)'


未完待续。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: