“This is the seventh day of my participation in the August More Text Challenge. For details, see: August More Text Challenge.”

A preface.

  • KVOSome details of
  • exploreKVOThe principle of
  • The customKVO

KVO official documentation link

I. Detailed analysis of KVO

About 1.contextOfficial details of the registration are shown belowListen to the callback

delete

static void *PersonNameContext = &PersonNameContext;

@interface LGViewController ()
@property (nonatomic, strong) LGPerson  *person;
@end

@implementation LGViewController


- (void)viewDidLoad {
    [super viewDidLoad];
     self.person  = [LGPerson new];
    [self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:PersonNameContext];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    self.person.name = @"test";

}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{

   if(context == PersonNameContext){
        NSLog(@"% @",change); }} - (void)dealloc{

 [self.person removeObserver:self forKeyPath:@"name" context:PersonNameContext];
}

Copy the code

Print the result

According to the official documentation, if we use KVO without removeObserver for example, if we go into a VC and there’s a singleton that holds and observes the VC’s properties even if the VC is released and the singleton doesn’t release and continues to observe the VC’s properties, the VC that was released before the properties change will continue to receive the message The wild pointer crashed because it had been released

Two.KVO details

1. Manually observe the properties

// Automatic switch
+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key{
    return NO;
}
- (void)setName:(NSString *)name
{
    [self willChangeValueForKey:@"name"];
    _name = name;
    [self didChangeValueForKey:@"name"];
}
Copy the code

2. Listen to a property that is controlled by two other variables

+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
    
    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
    if ([key isEqualToString:@"downloadProgress"]) {
        NSArray *affectingKeys = @[@"totalData"The @"writtenData"];
        keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
    }
    return keyPaths;
}

- (NSString *)downloadProgress{
    if (self.writtenData == 0) {
        self.writtenData = 10;
    }
    if (self.totalData == 0) {
        self.totalData = 100;
    }
    return [[NSString alloc] initWithFormat:@"%f".1.0f*self.writtenData/self.totalData];
}

// Listen to the downloadProgress property
[self.person addObserver:self forKeyPath:@"downloadProgress" options:(NSKeyValueObservingOptionNew) context:NULL];
// Change these two attributes
self.person.writtenData += 10;
self.person.totalData  += 1;

Copy the code

3. Observation of mutable arrays

  [self.person addObserver:self forKeyPath:@"dateArray" options:(NSKeyValueObservingOptionNew) context:PersonDataArrayContext];
  self.person.dateArray = [NSMutableArray arrayWithCapacity:1];
  
  
  [[self.person mutableArrayValueForKey:@"dateArray"] addObject:@"1"]; Print the result2021-08-15 11:16:20.182185+0800 001- KVO [39513:1527764] {
    kind = 1;
    new= (); }2021-08-15 11:16:25.016788+0800 001- KVO [39513:1527764] {
     indexes = "<_NSCachedIndexSet: 0x6000010b84e0>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
    kind = 2;
    new =     (
        1
     );
  }
  
  typedef NS_ENUM(NSUInteger, NSKeyValueChange) {
    NSKeyValueChangeSetting = 1,
    NSKeyValueChangeInsertion = 2,
    NSKeyValueChangeRemoval = 3,
    NSKeyValueChangeReplacement = 4}; kind=2Is of type INSERTCopy the code

Three.KVO principle

1. Kvo principle analysis

  1. Only properties are observedsetter
  2. The middle class –self.person -> LGPerson isaHave changedNSKVONotifying_LGPerson (LGPerson subclass)
  3. What are the things-method-propertiessetNickNameclassdealloc_isKVOA
  4. Inherit – rewrite – actual implementation
  5. Setter subclass– Parent class changednickNameThe value of
  6. willchangeOf the parent classsetter didChange
  7. NSKVONotifying_LGPersonWhether to remove+isaWill come back at the time of removal observation

// The listener is dynamically generatedNSKVONotifying_LGPerson

#pragma mark - iterates over 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

Iv.KVO principle

  • 1.isa->LGPerson->NSKVONotifying_LGPersondisappear
  • 1.1 Dynamic GenerationNSKVONotifying_LGPerson
  • 1.2 LGPerson VS NSKVONotifying_LGPersonFather and son
  • 1.3 NSKVONotifying_LGPersonWhat are the methods
  • 'setNickName' // Override the set methodCopy the code
  •   `class`
    Copy the code
  •   `dealloc`
    Copy the code
  •   `_isKVOA`
    Copy the code
  • 1.4 isaPoint back // through this method_isKVOA
  • 1.5 NSKVONotifying_LGPersonWhether to destroy

-2. Setter KVO instance method (setter)/class

  • 2.1 Members VS attributes

  • 2.2 Modifying LGPerson properties

#pragma mark - 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);
}
// Iterate over the methods of NSKVONotifying_LGPerson
[self printClassAllMethod:objc_getClass("NSKVONotifying_LGPerson")];

2021-08-15 12:20:25.681840+0800 002-- Discussion on KVO Principle [39748:1566211] setNickName:-0x7fff207bf03f
2021-08-15 12:20:25.682122+0800 002-- Discussion on KVO Principle [39748:1566211] class0x7fff207bdb49The 2021-08-15 12:20:25. 682305 + 0800-002KVODiscussion on principle [39748:1566211]dealloc0x7fff207bd8f7The 2021-08-15 12:20:25. 682472 + 0800-002KVODiscussion on principle [39748:1566211]_isKVOA0x7fff207bd8ef
Copy the code

removeThe former isNSKVONotifying_LGPerson removeAfter isLGPersoninstructionsisaRefers to the back