This is the 12th day of my participation in the August More Text Challenge. For details, see:August is more challenging

Hi 👋

  • Wechat: RyukieW
  • 📦 Technical article archive
  • 🐙 making
My personal project Mine Elic endless sky ladder Dream of books
type The game financial
AppStore Elic Umemi

First, the middle class

When it comes to KVO, you probably know that the generation class is an NSKVONotifying_XXX class. But you might not be able to answer any further questions.

1.1 Are intermediate classes dynamically generated or compiled?

Let’s see if we can get the NSKVONotifying_RYModel class by calling objc_getClass(“NSKVONotifying_RYModel”) before setting the observer.

After adding an observer

Conclusion: You can see that NSKVONotifying_RYModel is indeed dynamically generated, and not already exists after compilation.

1.2 The relationship between the intermediate class and this class

Verify by guessing whether NSKVONotifying_RYModel is a subclass of RYModel.

Print yourself with all subclasses

- (void)logSubClassChain:(Class)cls {
    int count = objc_getClassList(NULL.0);
    
    NSMutableArray *tempArr = @[cls].mutableCopy;
    Class *classes = (Class*)malloc(sizeof(Class) * count);
    objc_getClassList(classes, count);
    for (int i = 0; i < count; i++) {
        if (cls == class_getSuperclass(classes[i])) {
            [tempArr addObject:classes[i]];
        }
    }
    free(classes);
    NSLog(@"Classes : %@", tempArr);
}
Copy the code

It is found that the output after adding the observer is one more NSKVONotifying_RYModel, which proves that it is indeed a subclass of RYModel

Conclusion: The class dynamically created by KVO is a subclass of this class.

1.3 What is the lifecycle of intermediate classes? Will it be removed?

Conclusion: Remove the observer and the NSKVONotifying_XXX page does not exist after the observer is released.

Consideration: Since intermediate classes exist all the time once generated, it is not advisable to use KVO too much in theory.

1.4 What is the difference between the method list of an intermediate class and this class?

How to add a print method:

+ (void)logAllMethodOf:(Class)cls {
    NSLog(@"%s %@", __func__, 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 = method_getImplementation(method);
        NSLog(@"SEL: %@ IMP: %p".NSStringFromSelector(sel), imp);
    }
    free(methodList);
    NSLog(@"\n");
}
Copy the code

RYModel

  • SEL: name IMP: 0x100003210
  • SEL: .cxx_destruct IMP: 0x1000032a0
  • SEL: setName: IMP: 0x100003170
  • SEL: books IMP: 0x100003240
  • SEL: setBooks: IMP: 0x100003260

NSKVONotifying_RYModel

  • SEL: setBooks: IMP: 0x7fff212b9d2e
  • SEL: setName: IMP: 0x7fff212b9d2e
  • SEL: class IMP: 0x7fff2127cc06
  • SEL: dealloc IMP: 0x7fff212bf9f5
  • SEL: _isKVOA IMP: 0x7fff213e3e3a

1.5 Are methods of intermediate classes inherited or overridden?

Create a new subclass and override the methods of the superclass:

@implementation RYSubModel

- (void)setBooks:(NSMutableArray<NSString *> *)books {
    [super setBooks:books];
}

@end
Copy the code

Output:

// Subclass +[KVOTool logAllMethodOf:] RYSubModel SEL: setBooks: IMP: [KVOTool logAllMethodOf:] RYModel SEL: name IMP: 0x100003160 SEL:.cxx_destruct IMP: 0x1000031f0 SEL: setName: IMP: 0x1000030c0 SEL: books IMP: 0x100003190 SEL: setBooks: IMP: NSKVONotifying_RYModel +[KVOTool logAllMethodOf:] NSKVONotifying_RYModel SEL: setBooks: IMP: 0x7fff212b9d2e SEL: setName: IMP: 0x7fff212b9d2e SEL: class IMP: 0x7fff2127cc06 SEL: dealloc IMP: 0x7fff212bf9f5 SEL: _isKVOA IMP: 0x7fff213e3e3aCopy the code

1.6 Impact of the KVO switch on the method list

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    if ([key isEqualToString:@"name"]) {
        return NO;
    }
    return YES;
}
Copy the code

After the name automatic notification is stopped, there is no class setName in the method list of the intermediate class.

Two, ISA pointing transformation

Above we know that the object type being observed becomes a class an intermediate class. So how does it change?

2.1 change

Before adding an observer

After adding an observer

It is clear that the ISA orientation of the instance has changed.

2.2 recovery

Start removing observers:

Remove the last observer:

All observers removed:

Restored RYModel

Consider: This class is only restored by ISA if all Key is removed from the observer, if not completely removed?

One Key is left without removing the observer

The ISA point to the last instance is still the intermediate class.

The process of triggering notifications

We call the stack at the point where the KVO notification was received:

  • tool.ryuk.name = @”Ryukie”;
    • Foundation`_NSSetObjectValueAndNotify
      • Foundation`-[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:]
        • Foundation`-[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:maybeNewValuesDict:usingBlock:]
          • Foundation`NSKeyValueDidChange
            • Foundation`NSKeyValueNotifyObserver
              • -[KVOTool observeValueForKeyPath:ofObject:change:context:]

conclusion

Now, we know

  • Intermediate classes are dynamically generated using KVO
  • The intermediate class is inherited from this class and overrides some methods of this class
  • The automonitor switch has an effect on the structure of the intermediate class
  • The ISA orientation of the observed will change and will be restored after the removal of the observation Key.

👋 Welcome to 👋