添加監聽函數
// 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