What is the KVO
Official document Key-value observing is a mechanism that allows objects to be notified when specified properties of other objects change.
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
Copy the code
We usually pass NULL in the last argument context, for example
[self.person addObserver:self forKeyPath:@"nick" options:NSKeyValueObservingOptionNew context:NULL];
Copy the code
Context
A safer and more extensible approach is to use the context to ensure notifications you receive are destined for your A more secure and extensible approach is to use context to ensure that notifications received are addressed to observers and not to the superclass. Listing 1 Creating context for Pointers
static void *PersonAccountBalanceContext = &PersonAccountBalanceContext;
static void *PersonAccountInterestRateContext = &PersonAccountInterestRateContext;
Copy the code
Listing 2 Registering the inspector as an observer of the balance and interestRate properties
- (void)registerAsObserverForAccount:(Account*)account {
[account addObserver:self
forKeyPath:@"balance"
options:(NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld)
context:PersonAccountBalanceContext];
[account addObserver:self
forKeyPath:@"interestRate"
options:(NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld)
context:PersonAccountInterestRateContext];
}
Copy the code
Remove observer
An observer does not automatically remove itself when deallocated. The observed object continues to send notifications, oblivious to the state of the observer. However, a change notification, like any other message, sent to a released object, triggers a memory access exception. You therefore ensure that observers remove themselves before disappearing from The observer does not automatically remove itself when released. The observed object continues to send notifications, ignoring the status of the observer. However, like any other message, a change notification sent to a released object triggers a memory access exception. Therefore, you can ensure that the observer removes itself before it disappears from memory)
Automatic Change Notification Automatically
One to many: for example, the progress of downloading a video, writte and Total, will change
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:@"downloadProgress"]) {
NSArray *affectingKeys = @[@"totalData".@"writtenData"];
keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
}
return keyPaths;
}
Copy the code
Manual Change Notification Manual Change
+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key{
return NO;
}
Copy the code
- (void)setNick:(NSString *)nick{
[self willChangeValueForKey:@"nick"];
_nick = nick;
[self didChangeValueForKey:@"nick"];
}
Copy the code
Observation of mutable arrays
The protocol defines three different proxy methods for collection object access. each with a key and a key path variant: mutableArrayValueForKey: and mutableArrayValueForKeyPath:These return a proxy object that behaves like an NSMutableArray object. mutableSetValueForKey: and mutableSetValueForKeyPath:These return a proxy object that behaves like an NSMutableSet object. mutableOrderedSetValueForKey: and mutableOrderedSetValueForKeyPath:These return a proxy object that behaves like an NSMutableOrderedSet object.
[[self.person mutableArrayValueForKey:@"dateArray"] addObject:@ "1"];
Copy the code
Underlying principles of KVO
When adding an observer:When removing observers:We found that after adding KVO observers,self.person
Pointing to aNSKVONotifying_Person
When removing the observer, ISA refers back (but createdNSKVONotifying_Person
The class was not destroyed. We can guess that this is dynamically generated at run time. Then thisNSKVONotifying_Person
andLGPerson
What does it matter?
#pragmaMark - Traverses classes and subclasses
- (void)printClasses:(Class)cls{
// Total number of registered classes
int count = objc_getClassList(NULL.0);
// Create an array containing the given object
NSMutableArray *mArray = [NSMutableArray arrayWithObject:cls];
// Get all registered classes
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);
}
Copy the code
We print the subclass of the current Person before and after adding observers to Person;
[self printClasses:[Person class]];
[self.person addObserver:self forKeyPath:@"nickName" options:(NSKeyValueObservingOptionNew) context:NULL];
[self printClasses:[Person class]].Copy the code
It can be concluded that: currentNSKVONotifying_Person
isPerson
A subclass of
What does NSKVONotifying_Person contain
There are only a few key things for a class: properties, methods, and protocols
#pragmaMark-traversal method-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);
}
Copy the code
[self printClassAllMethod:objc_getClass("NSKVONotifying_Person")];
Copy the code
Overrides these four methods:setNickName
– class
– dealloc
– _isKVOA
Is it a member variable or a Property ivar VS Property that KVO looks at
Although NskVonoJustify _Person overwrites the set method, it leaves us wondering whether the observer is observing a member variable or a property. Continue to verify
@interface Person : NSObject{
@public
NSString *name;
}
@property (nonatomic.copy) NSString *nickName;
@end
Copy the code
[self.person addObserver:self forKeyPath:@"nickName" options:(NSKeyValueObservingOptionNew) context:NULL];
[self.person addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:NULL];
Copy the code
KVO callback
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey.id> *)change context:(void *)context{
NSLog(@ "% @",change);
}
Copy the code
Printout:Conclusion: No member variables are observed.
Setter method listening
We know from the above that KVO actually looks at the set method, what the set method does. Debug using LLDBA hit occurs when a change is observedbt
the 在touchesBegin
tosetNickName
Between the system calls three methods: 1.-[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:maybeNewValuesDict:usingBlock:]
2.-[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:]
3._NSSetObjectValueAndNotify
Click through the assembly code in the call stack to get a rough idea of the flow: when we start listening, one is generatedNSKVONotifying_Person
, four methods are generated simultaneously. When we assign a value to a set, the set is actuallyNSKVONotifying_Person
Set method, which is now processed in method 3willChangeValueForKey:
anddidChangeValueForKey:
The callback notification comes after the modification.