添加监听函数
// 1.kvo对属性的监听 [_person addObserver:self forKeyPath:NSStringFromSelector(@selector(name)) options:NSKeyValueObservingOptionNew context:nil]; // 2.kvo属性关联,这个咱们是监听dog的变化,那若是是dog属性的变化,若是不作处理,是监听不到的 [_person addObserver:self forKeyPath:NSStringFromSelector(@selector(dog)) options:NSKeyValueObservingOptionNew context:nil]; // 3.容器监听 [_person addObserver:self forKeyPath:NSStringFromSelector(@selector(mArr)) options:NSKeyValueObservingOptionNew context:nil];
触发代码指针
// 触发基本知识点:KVO得经过set方法才能够触发 NSString *name = NSStringFromSelector(@selector(name)); // 手动触发 这两个方法必须是成对的,而后回调observeValueForKeyPath方法,至于为何成对呢,猜测应该是苹果作了处理,点进去官方的注释也有说明这两个方法必须成对存在 // 若是想要手动触发须要在被监听类中实现+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key返回NO [_person willChangeValueForKey:name]; [_person didChangeValueForKey:name]; // kvc [_person setValue:@"123" forKey:name]; // 容器 NSMutableArray *arr1 = [_person mutableArrayValueForKey:NSStringFromSelector(@selector(mArr))]; [arr1 addObject:@"123"]; NSMutableArray *arr2 = [_person mutableArrayValueForKey:NSStringFromSelector(@selector(mArr))]; [arr2 replaceObjectAtIndex:0 withObject:@"1234"]; NSMutableArray *arr3 = [_person mutableArrayValueForKey:NSStringFromSelector(@selector(mArr))]; [arr3 removeAllObjects]; // 属性关联 // 属性关联须要在被监听类中实现+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key _person.dog.name = @"myDog";
被监听类中实现的代码code
// 这个方法不重写,就是默认返回YES,若是重写了返回NO,那么就是须要手动触发了,固然这个能够根据参数key来判断,区分监听的key是手动仍是自动 + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { return YES; } // 这个方法用于监听属性的属性的(属性关联) + (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key { if ([key isEqualToString:@"dog"]) { return [[NSSet alloc] initWithObjects:@"_dog.name", nil]; } else { return [super keyPathsForValuesAffectingValueForKey:key]; } }
移除监听orm
懒得写
1.在运行时的时候建立被监听类的子类server
/** 1.动态生成一个类 */ /** 1.1 动态生成一个类名 */ NSString *oldClassName = NSStringFromClass(self.class); NSString *newClassName = [@"KVO_" stringByAppendingString:oldClassName]; /** 定义一个类,继承传进来的类 */ Class MyClass = objc_allocateClassPair(self.class, newClassName.UTF8String, 0);
2.在子类中重写父类属性的set方法(因此KVO只能监听属性)对象
/** 添加set方法 */ class_addMethod(MyClass, @selector(setName:), (IMP)setName, "V@:@");
3.注册这个子类继承
/** 注册该类 */ objc_registerClassPair(MyClass);
4.修改当前被监听对象的isa指针指向子类递归
/** 修改isa指针 */ object_setClass(self, MyClass);
5.实现set函数ci
/** set方法 */ void setName(id self,SEL _cmd,NSString * newName){ /** 保存当前类型 */ Class class = [self class]; /** 调用父类方法 */ object_setClass(self, class_getSuperclass(class)); objc_msgSend(self, @selector(setName:),newName); /** 通知观察者 */ // 这里有个属性绑定,因此在添加观察者的时候,就是将这个观察者持有(就是使用属性绑定来持有) id observer = objc_getAssociatedObject(self, @"objc"); if (observer) { objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),@"name",self,@{@"new:":[self valueForKey:@"name"],@"kind:":@1},nil); } /** 改回子类 */ object_setClass(self, class); // kvo就是在set方法中调用 willChangeValueForKey: didChangeValueForKey:; 那咱们这里需不须要呢,固然不须要,由于咱们是本身实现了一套了,调这两句也没有用的 }
1.为何kvc能够触发,kvc的原理rem
2.建立一个子类,若是建立的子类的类名,项目中恰好存在呢
若是项目中存在,那个建立的子类会nil,那么这时候咱们可使用递归建立,为nil就在类名后拼1或者其余字符吧,直到成功为止
3.objc_msgSend(self, @selector(setName:),newName);
能够传值,那这个跟performSelector:
有什么关系,并且发现直接用objc_msgSend
还比performSelector
方便
4.为何oc的每一个方法都有两个隐式参数,这两个是哪里来的
oc调用方法是消息机制,表现形式就是objc_msgSend(self, @selector(setName:),newName);
那么每一个方法在调用的本质都是使用objc_msgSend
那这个恰好就须要传两个参数,调用者和方法编号,也就是isa和sel