Article directory
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
accessInstanceVariablesDirectly
the method returnsYES
, 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;isKey
NSUnknowKeyExpection
- If
accessInstanceVariablesDirectly
the method returnsNO
, it will be calledsetValue:forUndefinedKey:
andNSUnknowKeyExpection
an exception will be thrown;
2. KVC value principle valueForKey:
- First, the method will be searched in the order of
getKey
,key
,isKey
,_key
and the value will be directly called. - If not found, check
+ (BOOL)accessInstanceVariablesDirectly
the return value. If returnedNO
, throwNSUnknowKeyExpection
an exception directly; - If it is returned
YES
, search the member variables in the order of_key
,_isKey
,key
,isKey
and take the value if found; - If not found, call
valueForUndefinedKey
: throwNSUnknowKeyExpection
exception;
3. Attention
key
The value must be correct, if it is spelled incorrectly, an exception will occur.- When
key
the value of is not defined,valueForUndefinedKey
this method will be called. If you write this method yourself,key
if the value of is wrong, it will be called here. - Because classes can be nested repeatedly, there is
keyPath
a conceptkeyPath
of using.
numbers to link keys one by one, so that they can be accessed according to this path. NSArray
/NSSet
etc. are all supportedKVC
.- You can
KVC
access the private members of a custom type. - If a
nil
value is passed to a non-object, the methodKVC
will be calledsetNIlValueForKey
. We can override this method to avoid errors when passing nil. The object will not call this method, but will report an error directly. - When dealing with non-objects,
setValue
if the object to be assigned is a basic type, the value needs to be encapsulated intoNSNumber
orNSValue
type.valueForKey
When an object of id type is returned, the basic data type will also be encapsulated intoNSNumber
or typeNSValue
.valueForKey
It is possible to automatically encapsulate values into objects, butsetValue:forKey:
it is not possible. We must manually convert the value type intoNSNumber/NSValue
a 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)
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
if ([key isEqualToString:@"pr_age"]) {
self.age = (NSString *)value;
}
}
KVO uses
1. Introduction to KVO
KVO
The full name is Key Value Observing
: Key-value monitoring, which can be used to monitor changes in the attribute value of an object;
KVO
Apple provides a set of event notification mechanisms, KVO
and NSNotificationCenter
are both implementations of the iOS observer pattern.
Difference: NSNotificaionCenter
One-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,
context
in 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
Model
and 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
addObserver
the method,KVO
there 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. addObserver
The sum of KVOremoveObserver
needs to be in pairs. If it is repeated,remove
it will causeNSRangeException
a crash of the type. If it is forgotten, it will crash when the callbackremove
is received again after the observer is released .KVO
5. KVO usage scenarios
- For objects that change all the time, for example
colletionView
,items
always change dynamically, you can useKVO
listening objects at this time. AVFounditon
The playback progress and playback status obtained in ,AVPlayer
also need to be observed using KVO.
KVO principle
1. The essence of KVO is to change the call of the setter method
- Use the Runtime API to dynamically generate a subclass
NSKVONotifying_XXX
, and letinstance
the object's isa point to this new subclass andNSKVONotifying_XXX
thesuperclass
pointer to point to the original class; - The function that will be called when modifying
instance
the 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 _NSSetObjectValueAndNotify
function called () is called to implement notification of attribute changes.
_NSSetObjectValueAndNotify
The 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 observer
to observeValueForKeyPath
tell the listener that the property value has changed.
4. Manually call KVO
- Modify
automaticallyNotifiesObserversForKey
the return value of a class method; - Calling KVO mainly relies on two methods, calling the method before the attribute changes
willChangeValueForKey
, and calling the method after the attribute changesdidChangeValueForKey
;
5. Notes on the implementation of KVO
- When the observation object removes all listeners, the isa of the observation object will point to the original class.
- 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
- 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.
KVO summary
1. The essence of KVO
- Use
RuntimeAPI
dynamic generation to generate a subclass and letinstance
the objectisa
point to this new subclass - The function that is called when modifying
instance
the properties of an objectFoundation
_NSSetXXXValueAndNotify
- Then call the original setter method of the parent class to modify the value of the attribute.
- Finally called
didChangeValueForKey
, the listening method of the listener (Oberser) will be triggered internally.(observerValueForKeyPath:ofObject:change:context:);
2. Summary of KVO implementation process
addObserver:forKeyPath:options:context:context
When calling, a subclass of the corresponding class of the object (observed object) will be automatically generated and registered, namedNSKVONotify_Class
, and the isa pointer of the object will point to this new class.- Implement 4 methods inside this subclass -
set
the method of the observed attribute,class
method,isKVO
,delloc
. - 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 calleddidChangeValueForKey
.
willChangeValueForKey和didChangeValueForKey
They need to appear in pairs to take effect, anddidChangeValueForKey
the observerobserveValueForKeyPath: ofObject:
method will be called in them.
- Override
class
methods to avoid external awareness of the existence of subclasses and preventisKindOfClass
errors when using judgments. isKVO
Method serves asKVO
an indicator of whether a function can be achieved.delloc
isa
restore pointer inside
KVO & KVC Problem Summary
3.1 How to call methods after isa mixing?
- When calling the monitoring property setting method, for example
setAge
:,NSKVONotify_Person
the corresponding property setting method will be called first. - Calling the non-listening property setting method, for example
test
, will find the class objectNSKVONotify_Person
through , and then call the method.superclass
Person
[Person test]
3.2 Why rewrite the class method inside the generated subclass?
If the class method is not overridden, when the object calls class
the 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 class
the method is NSObject
a 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 instance
of 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.setter
setvalue:forkey:
willchangevalueforkey
didchangevalueforkey
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.
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.