Summary of basic principles of iOS
- The context context parameter in the observer prevents duplication of names (attributes of the same name observed by multiple objects), performance, code readability, and security
- The observer must be removed in the dealloc method, otherwise the program will crash.
[self.student addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
- (void)dealloc{
[self.student removeObserver:self forKeyPath:@"name" context:NULL];
}
Copy the code
- The singleton’s attribute observer observes the same attribute name in both controllers. If it is not removed, it will cause a wild pointer, which observer cannot be determined and crash
[self.student addObserver:self forKeyPath:@”name” options:(NSKeyValueObservingOptionNew) context:NULL]; In, no circular reference is generated, and the attribute observer added underneath is the weak-saved self
The singleton object always exists in the memory, and the name attribute is a copy. When the name value changes, both observers will receive the notification, and it is not clear which object needs to be processed, which belongs to the wild notification.
KVO’s first experience
KVO steps:
- Add observation
observe
The callback- Change the value of the observation property in the appropriate place
- in
dealloc
Inside remove observation
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [LGPerson new];
[self.person addObserver:self forKeyPath:@"nick" options:NSKeyValueObservingOptionNew context:NULL];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.person.nick = [NSString stringWithFormat:@"%@+",self.person.nick];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%@",change);
}
- (void)dealloc{
[self.person removeObserver:self forKeyPath:@"dateArray"];
}
Copy the code
Other uses of KVO
1. Switch between manual and automatic
- Auto switch (default)
+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key{
return YES;
}
Copy the code
- Manual switch
+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key{
return NO;
}
- (void)setNick:(NSString *)nick{
[self willChangeValueForKey:@"nick"];
_nick = nick;
[self didChangeValueForKey:@"nick"];
}
Copy the code
2. Path processing
@implementation LGPerson -- writtenData/totalData + (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{ NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; if ([key isEqualToString:@"downloadProgress"]) { NSArray *affectingKeys = @[@"totalData", @"writtenData"]; keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys]; } return keyPaths; } - (NSString *)downloadProgress{if (self.writtenData == 0) {self.writtenData = 1.0; } if (self.totalData == 0) { self.totalData = 100; } return [[nsstrings alloc] initWithFormat: @ "% f", 1.0 f * self writtenData/self totalData]; } @end @implementation LGViewController - (void)viewDidLoad { [super viewDidLoad]; self.person = [LGPerson new]; / / 4: [self.person addObserver:self forKeyPath:@"downloadProgress" options:(NSKeyValueObservingOptionNew) context:NULL]; } @endCopy the code
3. Listen on array properties
- For collection type, it belongs to key-value observation, based on KVC, can not directly add elements, need to save the array mutableArrayValueForKey
@interface LGPerson : NSObject @property (nonatomic, strong) NSMutableArray *dateArray; @end @implementation LGViewController - (void)viewDidLoad { [super viewDidLoad]; self.person = [LGPerson new]; Self.person. dateArray = [NSMutableArray arrayWithCapacity:1]; [self.person addObserver:self forKeyPath:@"dateArray" options:(NSKeyValueObservingOptionNew) context:NULL]; } - (void)touch began :(NSSet< touch *> *)touches withEvent:(UIEvent *)event{// KVC set array [[self.person mutableArrayValueForKey:@"dateArray"] addObject:@"1"]; }Copy the code
/* Possible values in the NSKeyValueChangeKindKey entry in change dictionaries. See the comments for -observeValueForKeyPath:ofObject:change:context: for more information. */ typedef NS_ENUM(NSUInteger, NSKeyValueChange) { NSKeyValueChangeSetting = 1, Attribute NSKeyValueChangeInsertion = 2, collection types NSKeyValueChangeRemoval = 3, NSKeyValueChangeReplacement = 4,};Copy the code
Principle analysis of KVO
KVO can only observe properties, which have setter methods, not member variables
- Click the screen below to output only property changes, no changes observed in member variables
KVO forms the intermediate class
- The isa of the person object points to the LGPerson class. After the middle class is generated, ISA no longer points to the LGPerson class. After the observer is added, isa of the Person object points to the derived middle class NSKVONotifying_LGPerson
- To obtain the current ISA status, use object_getClassName(self.person), print the following
nickName
addaddObserver
The ISA pointing class changes before and after
View the subclass before and after adding observer first traverses the class and view the subclass
Prints subclass changes before and after addObserver is added
What’s in the middle class NSKVONotifying_LGPerson? We iterate through the method -ivar-property
Print result:
All methods in NSKVONotifying_LGPerson:setNickName:
.class
.dealloc
(Release monitor),_isKVOA
(Whether KVO or not)
SetNickName: is the method inherited from LGPerson or overridden?
- Create a subclass of LGPerson, LGStudent, and check the methods in LGStudent. If LGStudent has all the methods in NSKVONotifying_LGPerson (
setNickName:
.class
.dealloc
(Release monitor),_isKVOA
(KVO), then all methods in NSKVONotifying_LGPerson come from inheritance
The print finds that there are no methods in LGStudent, so all methods in NSKVONotifying_LGPerson come from overrides
Both of these methods are derived from overwriting, so overwriting setName in LGStudent: can be verified by printing out the setName: method in LGStudent
When you add an observer isa points to the middle class NSKVONotifying_LGPerson, when will ISA come back to LGPerson? Was it when you removed the observer? See the pointer before and after removing the observer in the dealloc destructor.
- After removing the observer, continue traversing
LGPerson
Classes and subclasses
Print result:
After removing the observer, NSKVONotifying_LGPerson will always exist after it is registered in memory, so as to prevent the program from adding observers and repeatedly creating NSKVONotifying_LGPerson memory space, which will waste performance
- So we’re going to look at subclasses
setter
, or superclasssetter
changenickName
值
Watchpoint set variable self->_person->_nickName
Find setNickName by setting the watch point: between willChange and didChange
Call [super setNickName:] from NSKVONotifying_LGPerson
Custom KVO
Realize the addObserver
Verify that setter methods exist
- Do not let instance member variables in. If you observe the member variable name, an error is reported
Dynamically subclassing
- The ivAR space is initialized and allocated in read_images. The ivar space exists in RO, clean memory, and cannot be added.
- Methods and attributes added In RWE, dirty Memory, can be added.
Pointing to the isa
Changes in property values are notified to custom methods
- Came to
lg_setter
Method, change the nickName value of the parent class, and send a message to the parent classsetNickName:
, redefineobjc_msgSendSuper
, pass in three parametersobjc_super
Parent structure pointer,_cmd(setNickName
Method:,newValue
The newly changed value goes into the custom subclassLGKVONotifying_LGPerson
The parent classLGPerson
thesetNickName:
In the method
- Disable the number of strict LLVM verification parameters. No further action is required
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
Methods an error
- The system observer self.person.class should point to LGPerson before and after the observation, while the custom observer self.person.class points to LGKVONotifying_LGPerson
Class(object_getClass(self));
One note: replacing object_getClass(self) with [self class] in the following two codes will cause an error
struct objc_super superStruct = {
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self)),
};
Copy the code
Class lg_class(id self,SEL _cmd){
return class_getSuperclass(object_getClass(self));
}
Copy the code
- The benefits of objc_msgSendSuper message sending: generic, encapsulated, resolved dependencies that don’t depend on the class LGPerson or NSObject, whether the class is NSObject or LGPerson
- The method in setNickName: receives the nickName and sends a message to notify the observer controller VC when the attribute value changes
- Does adding an associated object create a circular reference? No, because the associated object is stored in a hash table, there is no holding, one-to-one correspondence saving, controller VCpop can still enter the dealloc method, there is no circular reference
If you want to view multiple attributes, this will save multiple observer VC, so you need to optimize.
- Create a LGKVOInfo class to hold the observer information
‘
- Changed the logic for saving observer information in categories
Remove observer
The above custom KVO is through the method of value transmission. After the change of value is observed, it is notified in different methods. Is there a functional way y=f(x), which is coupled with the adding observer?