02-KVO的基本使用和本质

runtime的强大之处:在运行的过程中可以动态生成一个类

一、KVO的基本使用

  • 1.MJPerson类
-----------------MJPerson.h----------------
#import 

@interface MJPerson : NSObject
@property (assign, nonatomic) int age;
@property (assign, nonatomic) int height;
@end

-------------------MJPerson.m-------------------
#import "MJPerson.h"

@implementation MJPerson

@end
  • 2.ViewController
----------------------ViewController.h--------------------
#import 

@interface ViewController : UIViewController

@end

----------------------ViewController.m--------------------
#import "ViewController.h"
#import "MJPerson.h"

@interface ViewController ()
@property (strong, nonatomic) MJPerson *person1;
@property (strong, nonatomic) MJPerson *person2;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person1 = [[MJPerson alloc] init];
    self.person1.age = 1;
    self.person1.height = 11;
    
    self.person2 = [[MJPerson alloc] init];
    self.person2.age = 2;
    self.person2.height = 22;
    
    // 给person1对象添加KVO监听
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
    [self.person1 addObserver:self forKeyPath:@"height" options:options context:@"456"];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    self.person1.age = 20;
    self.person2.age = 20;
    
    self.person1.height = 30;
    self.person2.height = 30;
}

- (void)dealloc {
    [self.person1 removeObserver:self forKeyPath:@"age"];
    [self.person1 removeObserver:self forKeyPath:@"height"];
}

// 当监听对象的属性值发生改变时,就会调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
}

@end
  • 3.打印
-------------- Interview01[28489:1594950] 监听到的age属性值改变了 - {
    kind = 1;
    new = 20;
    old = 1;
} - 123
--------------- Interview01[28489:1594950] 监听到的height属性值改变了 - {
    kind = 1;
    new = 30;
    old = 11;
} - 456

二、KVO的本质

  • 使用KVO监听的对象,系统利用runtime动态的创建一个MJPerson的子类NSKVONotify_MJPerson
    1.在子类的setAge:方法调用_NSSetIntValueAndNotify()方法
    2.在_NSSetIntValueAndNotify方法中调用父类[super setAge:age]给_age赋值
    3.赋值完后调用- (void)didChangeValueForKey:(NSString *)key方法
    4.在- (void)didChangeValueForKey:(NSString *)key方法里通知监听器调用[oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];

伪代码如下:

  • 1、MJPerson类
-------------------------MJPerson.h--------------------
#import 

@interface MJPerson : NSObject
@property (assign, nonatomic) int age;
@end
--------------------------MJPerson.m--------------------
#import "MJPerson.h"

@implementation MJPerson

- (void)setAge:(int)age
{
    _age = age;
    
    NSLog(@"setAge:");
}
@end
  • 2、NSKVONotify_MJPerson类 (Foundataion框架下的继承MJPerson的子类)
------------------------NSKVONotify_MJPerson.h-------------------------
#import "MJPerson.h"

@interface NSKVONotifying_MJPerson : MJPerson

@end

------------------------NSKVONotify_MJPerson.m-------------------------
#import "NSKVONotifying_MJPerson.h"

@implementation NSKVONotifying_MJPerson

- (void)setAge:(int)age
{
    _NSSetIntValueAndNotify();
}

// 伪代码
void _NSSetIntValueAndNotify()
{
    [self willChangeValueForKey:@"age"];
    [super setAge:age];
    [self didChangeValueForKey:@"age"];
}

- (void)didChangeValueForKey:(NSString *)key
{
    // 通知监听器,某某属性值发生了改变
    [oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}

@end
  • 3、ViewController
--------------------ViewController.h--------------------
#import 

@interface ViewController : UIViewController

@end

----------------------ViewController.m-------------------
#import "ViewController.h"
#import "MJPerson.h"
#import 

@interface ViewController ()
@property (strong, nonatomic) MJPerson *person1;
@property (strong, nonatomic) MJPerson *person2;
@end

// 反编译工具 - Hopper

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person1 = [[MJPerson alloc] init];
    self.person1.age = 1;
    
    self.person2 = [[MJPerson alloc] init];
    self.person2.age = 2;
    
    // 给person1对象添加KVO监听
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
    
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // NSKVONotifying_MJPerson是使用Runtime动态创建的一个类,是MJPerson的子类
    // self.person1.isa == NSKVONotifying_MJPerson
    [self.person1 setAge:21];
    
    // self.person2.isa = MJPerson
//    [self.person2 setAge:22];
}

- (void)dealloc {
    [self.person1 removeObserver:self forKeyPath:@"age"];
}

// 当监听对象的属性值发生改变时,就会调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
}

@end
  • 验证KVO添加监听和未添加监听的本质
  • 1.验证添加KVO和未添加的实例对象isa指向的类对象

+2.验证添加KVO和未添加KVO类对象isa指向的元类对象

反编译工具:Hopper

完整代码如下:

  • 1、MJPerson类
-----------------------MJPerson.h-----------------------
#import 

@interface MJPerson : NSObject
@property (assign, nonatomic) int age;
@end

-------------------------MJPerson.m--------------------
#import "MJPerson.h"

@implementation MJPerson

- (void)setAge:(int)age
{
    _age = age;
    
    NSLog(@"setAge:");
}

- (void)willChangeValueForKey:(NSString *)key
{
    [super willChangeValueForKey:key];
    
    NSLog(@"willChangeValueForKey");
}

- (void)didChangeValueForKey:(NSString *)key
{
    NSLog(@"didChangeValueForKey - begin");
    
    [super didChangeValueForKey:key];
    
    NSLog(@"didChangeValueForKey - end");
}

@end

  • ViewController类
-----------------------ViewController.h----------------------
#import 

@interface ViewController : UIViewController

@end
-------------------------ViewController.m------------------------
#import "ViewController.h"
#import "MJPerson.h"
#import 

@interface ViewController ()
@property (strong, nonatomic) MJPerson *person1;
@property (strong, nonatomic) MJPerson *person2;
@end

// 反编译工具 - Hopper

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person1 = [[MJPerson alloc] init];
    self.person1.age = 1;
    
    self.person2 = [[MJPerson alloc] init];
    self.person2.age = 2;
    
    // 给person1对象添加KVO监听
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
    
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // NSKVONotifying_MJPerson是使用Runtime动态创建的一个类,是MJPerson的子类
    // self.person1.isa == NSKVONotifying_MJPerson
    [self.person1 setAge:21];
    
    // self.person2.isa = MJPerson
//    [self.person2 setAge:22];
}

- (void)dealloc {
    [self.person1 removeObserver:self forKeyPath:@"age"];
}

// 当监听对象的属性值发生改变时,就会调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
}

@end
  • class、dealloc、_isKVOA的作用和实现

添加kvo监听后系统会自动生成隐藏NSKVONotifying_MJPerson类,里面的方法时看不到的。

  • 打印类对象的所有方法名:
#import "ViewController.h"
#import "MJPerson.h"
#import 

@interface ViewController ()
@property (strong, nonatomic) MJPerson *person1;
@property (strong, nonatomic) MJPerson *person2;
@end

//@implementation NSObject
//
//- (Class)class
//{
//    return object_getClass(self);
//}
//
//@end

// 反编译工具 - Hopper

@implementation ViewController

//传入一个类对象,打印这个类对象的所有方法名
- (void)printMethodNamesOfClass:(Class)cls
{
    unsigned int count;
    // 获得方法数组
    Method *methodList = class_copyMethodList(cls, &count);
    
    // 存储方法名
    NSMutableString *methodNames = [NSMutableString string];
    
    // 遍历所有的方法
    for (int i = 0; i < count; i++) {
        // 获得方法
        Method method = methodList[i];
        // 获得方法名
        NSString *methodName = NSStringFromSelector(method_getName(method));
        // 拼接方法名
        [methodNames appendString:methodName];
        [methodNames appendString:@", "];
    }
    
    // 释放(c语言创建的对象要手动释放)
    free(methodList);
    
    // 打印方法名
    NSLog(@"%@ %@", cls, methodNames);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person1 = [[MJPerson alloc] init];
    self.person1.age = 1;
    
    self.person2 = [[MJPerson alloc] init];
    self.person2.age = 2;
    
    // 给person1对象添加KVO监听
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
    
    [self printMethodNamesOfClass:object_getClass(self.person1)];
    [self printMethodNamesOfClass:object_getClass(self.person2)];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//    [self.person1 setAge:21];
    
    [self.person1 willChangeValueForKey:@"age"];
    [self.person1 didChangeValueForKey:@"age"];
}

- (void)dealloc {
    [self.person1 removeObserver:self forKeyPath:@"age"];
}

// observeValueForKeyPath:ofObject:change:context:
// 当监听对象的属性值发生改变时,就会调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
}

@end

你可能感兴趣的:(02-KVO的基本使用和本质)