【iOS】KVC &KVO Summary

KVC

1. KVC assignment principle setValue:forKey:

  • First, the method will be searched in the order of and . If the method is found, the method will be called directly and assigned a value setKey;_setKey
  • If the method is not found, call + (BOOL)accessInstanceVariablesDirectly(whether the member variable can be directly accessed, and YES will be returned by default);
  • If accessInstanceVariablesDirectlythe method returns YES, the member variables are searched in the order of _key, _isKey, key, and the values ​​are directly assigned if found. If not found, an exception is thrown;isKeyNSUnknowKeyExpection
  • If accessInstanceVariablesDirectlythe method returns NO, it will be called setValue:forUndefinedKey:and NSUnknowKeyExpectionan exception will be thrown;
    Insert image description here

2. KVC value principle valueForKey:

  1. First, the method will be searched in the order of getKey, key, isKey, _keyand the value will be directly called.
  2. If not found, check + (BOOL)accessInstanceVariablesDirectlythe return value. If returned NO, throw NSUnknowKeyExpectionan exception directly;
  3. If it is returned YES, search the member variables in the order of _key, _isKey, key, isKeyand take the value if found;
  4. If not found, call valueForUndefinedKey: throw NSUnknowKeyExpectionexception;
    Insert image description here

3. Attention

  1. keyThe value must be correct, if it is spelled incorrectly, an exception will occur.
  2. When keythe value of is not defined, valueForUndefinedKeythis method will be called. If you write this method yourself, keyif the value of is wrong, it will be called here.
  3. Because classes can be nested repeatedly, there is keyPatha concept keyPathof using .numbers to link keys one by one, so that they can be accessed according to this path.
  4. NSArray/ NSSetetc. are all supported KVC.
  5. You can KVCaccess the private members of a custom type.
  6. If a nilvalue is passed to a non-object, the method KVCwill be called setNIlValueForKey. We can override this method to avoid errors when passing nil. The object will not call this method, but will report an error directly.
  7. When dealing with non-objects, setValueif the object to be assigned is a basic type, the value needs to be encapsulated into NSNumberor NSValuetype. valueForKeyWhen an object of id type is returned, the basic data type will also be encapsulated into NSNumberor type NSValue. valueForKeyIt is possible to automatically encapsulate values ​​into objects, but setValue:forKey:it is not possible. We must manually convert the value type into NSNumber/NSValuea type before passing itinitWithBool:(BOOL)value。

4. KVC’s batch storage and retrieval of values


@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *sex;
@property (nonatomic, strong) NSString *age;
@end

// KVC批量赋值
- (void)kvc_setKeys {
    
    
    NSDictionary *kvc_dict = @{
    
    @"name": @"clearlove", @"sex": @"male", @"pr_age": @"21"};
    Person *pr = [[Person alloc] init];
    [pr setValuesForKeysWithDictionary:kvc_dict];
    NSLog(@"%@", pr.age);
}

// KVC批量取值
- (void)kvc_getValues {
    
    
    Person *pr = [[Person alloc] init];
    [pr setValue:@"mekio" forKey:@"name"];
    [pr setValue:@"male" forKey:@"sex"];
    [pr setValue:@"120" forKey:@"pr_age"];
    NSDictionary *pr_dict = [pr dictionaryWithValuesForKeys:@[@"name", @"age", @"sex"]];
    NSLog(@"%@", pr_dict);
}

If there is a key that does not correspond to the attribute when getting or assigning a value, rewrite - (void)setValue:(id)value forUndefinedKey:(NSString *)key the method
(the above code is the part that has been rewritten)

Please add image description

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    
    
    if ([key isEqualToString:@"pr_age"]) {
    
    
        self.age = (NSString *)value;
    }
}

KVO uses

1. Introduction to KVO

KVOThe full name is Key Value Observing: Key-value monitoring, which can be used to monitor changes in the attribute value of an object;

KVOApple provides a set of event notification mechanisms, KVOand NSNotificationCenterare both implementations of the iOS observer pattern.

Difference: NSNotificaionCenterOne-to-many can exist, while KVO is a one-to-one relationship.

2. KVO monitoring steps

Register to listen

Register KVO through [addObserver:forKeyPath:options:context:]the method so that you can receive the change event of the keyPath attribute;

observer: Observer, an object that monitors property changes. The object must implement the observeValueForKeyPath:ofObject:change:context: method.
keyPath: The name of the attribute to be observed. Must be consistent with the name of the attribute declaration.
options: In the callback method, the old value or new value of the attribute of the observer is received, etc., the KVO mechanism is configured, and the timing of the KVO notification and the content of the notification are modified: pass in any type of object,
contextin the code of "receive message callback" This object can be received in KVO, which is a value-passing method in KVO.

Monitoring implementation

[observeValueForKeyPath:ofObject:change:context:]Implement KVO monitoring through methods ;

keyPath: Attributes of the observed object
object: Observed object
change: Dictionary, which stores related values ​​and returns new values ​​and old values ​​according to the enumeration passed in options
context: the value passed by context when registering an observer

Remove listening

When monitoring is no longer needed, [removeObserver:forKeyPath:]remove monitoring through methods;

example

  • Implement a button in the view to monitor the person's name changes.
// kvo监听
- (void)kvo_pr {
    
    
    
    NSDictionary *kvc_dict = @{
    
    @"name": @"clearlove", @"sex": @"male", @"pr_age": @"21"};
    self.pr_1 = [[Person alloc] init];
    [self.pr_1 setValuesForKeysWithDictionary:kvc_dict];
    
    [self.pr_1 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    
    
    NSLog(@"new_name == %@", [change valueForKey:@"new"]);
}
- (void)dealloc {
    
    
    [self.pr_1 removeObserver:self forKeyPath:@"name"];
}
  • If you want to control the automatic calling process of the current object, that is, the KVO call initiated by the above two methods, you can override the following method. If the method returns YES, it means that the listening event of the related object can be called. If it returns NO, it means that the listening event of the related object cannot be called.
  • If name cannot be monitored, override (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key the method in persn's implementation
// 禁止监听某个属性
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    
    
    if ([key isEqualToString:@"name"]) {
    
    
        NSLog(@"forbid Nostifity pr.name");
        return NO;
    }
    return [super automaticallyNotifiesObserversForKey: key];
}

3. KVO value transfer

  • Any type of object can be passed in through methods context. This object can be received in the code that receives the message callback. It is a value-passing method in KVO.
  • Communication between Modeland via KVO .Controller

4. KVO Attention

  • The call [removeObserver:forKeyPath:]needs to be made before the observer disappears, otherwise it will cause a crash.
  • After calling addObserverthe method, KVOthere will be no strong reference to the observer, so you need to pay attention to the life cycle of the observer, otherwise it will cause a crash caused by the observer being released.
  • The observer needs to implement observeValueForKeyPath:ofObject:change:context:the method. This method will be called when the KVO event arrives. If it is not implemented, it will cause a crash.
  • addObserverThe sum of KVO removeObserverneeds to be in pairs. If it is repeated, removeit will cause NSRangeExceptiona crash of the type. If it is forgotten, it will crash when the callback removeis received again after the observer is released .KVO

5. KVO usage scenarios

  1. For objects that change all the time, for example colletionView, itemsalways change dynamically, you can use KVOlistening objects at this time.
  2. AVFounditonThe playback progress and playback status obtained in , AVPlayeralso need to be observed using KVO.

KVO principle

1. The essence of KVO is to change the call of the setter method

  1. Use the Runtime API to dynamically generate a subclass NSKVONotifying_XXX, and let instancethe object's isa point to this new subclass and NSKVONotifying_XXXthe superclasspointer to point to the original class;
  2. The function that will be called when modifying instancethe properties of the object ;Foundation_NSSetXXXValueAndNotify

2. Internal implementation principle of _NSSetXXXValueAndNotify

- setName:最主要的重写方法,set值时调用通知函数
- class:返回原来类的class
- dealloc
- _isKVOA判断这个类有没有被KVO动态生成子类

- (void)setClassName:(NSString *)className {
    
    

}

- (Class)class {
    
    
- 这是为了保证该中间类在外部使用时可以替代原始类,实现完全透明的KVO功能。
    return [testClass class];
}

- (void)dealloc {
    
    
    // 收尾工作
}

- (BOOL)_isKVOA {
    
    
- 添加一个名为_isKVOA的实例变量**,用于标识该对象是否支持KVO机制。
    return YES;
}



Rewrite the Class method : This is to ensure that the intermediate class can replace the original class when used externally to achieve completely transparent KVO functionality.

Add an instance variable named _isKVOA to identify whether the object supports the KVO mechanism.

3. _NSSetObjectValueAndNotify

During the specific implementation process, the system will dynamically generate an intermediate class that inherits from the original class _NSSetXXXValueAndNotify, and in the initialization method of the class, a _NSSetObjectValueAndNotifyfunction called () is called to implement notification of attribute changes.

_NSSetObjectValueAndNotifyThe implementation process of () function is as follows:

a) will be called first willChangeValueForKey
b) call the original setter implementation and then assign a value to the property
c) finally call didChangeValueForKey
d) finally call observerto observeValueForKeyPathtell the listener that the property value has changed.

4. Manually call KVO

  1. Modify automaticallyNotifiesObserversForKeythe return value of a class method;
  2. Calling KVO mainly relies on two methods, calling the method before the attribute changes willChangeValueForKey, and calling the method after the attribute changes didChangeValueForKey;

5. Notes on the implementation of KVO

  1. When the observation object removes all listeners, the isa of the observation object will point to the original class.
  2. When all the monitoring objects of the observation object are removed, the dynamically generated class will not be unregistered, but will be used during the next observation to avoid repeatedly creating intermediate subclasses.

6. KVO class pointer exchange

  1. isa-swizzling (class pointer swapping) is to point the isa pointer of a current instance object to a newly constructed intermediate class, and do hook methods or other things on this newly constructed intermediate class, so that it will not affect other aspects of this class. Instance object, only affects the current instance object.
    Insert image description here

KVO summary

1. The essence of KVO

  1. Use RuntimeAPIdynamic generation to generate a subclass and let instancethe object isapoint to this new subclass
  2. The function that is called when modifying instancethe properties of an objectFoundation_NSSetXXXValueAndNotify
  3. Then call the original setter method of the parent class to modify the value of the attribute.
  4. Finally called didChangeValueForKey, the listening method of the listener (Oberser) will be triggered internally.(observerValueForKeyPath:ofObject:change:context:);

2. Summary of KVO implementation process

  1. addObserver:forKeyPath:options:context:contextWhen calling, a subclass of the corresponding class of the object (observed object) will be automatically generated and registered, named NSKVONotify_Class, and the isa pointer of the object will point to this new class.
  2. Implement 4 methods inside this subclass - setthe method of the observed attribute, classmethod, isKVO, delloc.
  3. The most important thing is that in the set method, it is called first willChangeValueForKey, then the original setter method is called to assign a value to the member variable , and finally called didChangeValueForKey.
  • willChangeValueForKey和didChangeValueForKeyThey need to appear in pairs to take effect, and didChangeValueForKeythe observer observeValueForKeyPath: ofObject: method will be called in them.
  1. Override classmethods to avoid external awareness of the existence of subclasses and prevent isKindOfClasserrors when using judgments.
  2. isKVOMethod serves as KVOan indicator of whether a function can be achieved.
  3. dellocisarestore pointer inside

KVO & KVC Problem Summary

3.1 How to call methods after isa mixing?

  1. When calling the monitoring property setting method, for example setAge:, NSKVONotify_Personthe corresponding property setting method will be called first.
  2. Calling the non-listening property setting method, for example test, will find the class object NSKVONotify_Personthrough , and then call the method.superclassPerson[Person test]

3.2 Why rewrite the class method inside the generated subclass?

If the class method is not overridden, when the object calls classthe method, it will search for the method in its own method cache list, method list, parent class cache, and method list all the way up. Because classthe method is NSObjecta method in, if it is not overridden, it will eventually May return NSKVONotifying_Person, the class will be exposed.

3.3 Will directly modifying the value of a member variable trigger KVO?

KVO will not be triggered. The essence of KVO is to replace the implementation of the setter method, so KVO will only be triggered if modified through the set method.

3.4 Will KVC trigger KVO when modifying attributes?

Yes, although setvalue:forkey:the method may not necessarily trigger the : method instanceof the instance object , when changing the value of the member variable, it will be manually called , and the callback method of the listener will be triggered.settersetvalue:forkey:willchangevalueforkeydidchangevalueforkey

3.5 How does KVO monitor changes in array elements?

  • KVO can only monitor changes in the length of the array, but not changes in internal objects. We can manually modify the elements inside the array with KVC to achieve the goal.Please add image description

We can add elements to the array through KVC, so that we can monitor it. Obtain the proxy object through KVC's mutableArrayValueForKey: and other methods. When the internal object of the proxy object changes, the KVO listening method will be called back. Collection objects contain NSArray and NSSet.

Guess you like

Origin blog.csdn.net/weixin_61639290/article/details/131968513