KVC 、KVO、 Category、objc_associationPolicy

KVO
  • KVO 全程是key-value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变
  • 利用runtimeAPI动态生成一个子类,并且让instance的ISA 指向这个全新的子类
    当修改instance对象的属性时 ,会在新的子类中的set方法里调用Foundation的_NSSetxxxvaluedAndNotify函数
    然后调用 wilChangeValuedForKey: ——>父类原来的setter——>didChangeValueforkey 方法 ,此方法内部会出发监听器(Observer)的监听方法(ObserverValueForKeyPath:ofObject:change:context)
KVC
  • key-value codeing 键值编码 ,可以通过一个key,访问某个属性

  • setValue:forKey:的原理


    KVC 、KVO、 Category、objc_associationPolicy_第1张图片
    image.png
  • valueForKey:的原理


    KVC 、KVO、 Category、objc_associationPolicy_第2张图片
    image.png
Category实现原理
  • category编译之后的底层结构是struct_category_t ,里面存储着对象方法、属性、类方法、协议信息
  • 在程序运行的时候,runtime会将category的数据,合并到类信息中(类对象、元类对象中)即runtime 把category的方法、属性、协议等合并到一个大数组总,后面参与编译的category数据,会在数组的前面;合并后的分类数据(方法、协议、属性),插入到类原来数据的前面

Category和Class Extension的区别是什么?
Class Extension在编译的时候,它的数据就已经包含在类信息中
Category是在运行时,才会将数据合并到类信息中

Loda 和 initialize 方法

######## load

  • load方法会在runtime加载类、分类的时候调用
  • 每个类、分类的load方法在程序运行过程中只调用一次
  • 调用顺序:
  • 1 : 先调用类的load 方法
    • 按照编译先后顺序调用(先编译,先调用)
    • 调用子类的load方法之前会先调用父类的load方法
  • 2 : 在调用分类的load方法
    • 先编译,先调用(按照编译的先后顺序)

######## initialize

  • initialize方法会在第一次接受消息的时候调用
  • 调用顺序: 先调用父类的+initialize方法,在调用子类的+initialize方法(先初始化父类,在初始化子类,每个类之只会初始化一次)
  • +initialize和load方法的区别: +initialize是通过objc_msgSend进行调用的。若果子类没有实现+initialize,会调用父类的+initialize(所以父类的initialize可能会被调用多次),如果分类实现了+initialize,就会覆盖本身的+initialize调用
load、initialize方法的区别什么?
1.调用方式
1> load是根据函数地址直接调用
2> initialize是通过objc_msgSend调用

2.调用时刻
1> load是runtime加载类、分类的时候调用(只会调用1次)
2> initialize是类第一次接收到消息的时候调用,每一个类只会initialize一次(父类的initialize方法可能会被调用多次)

load、initialize的调用顺序?
1.load
1> 先调用类的load
a) 先编译的类,优先调用load
b) 调用子类的load之前,会先调用父类的load

2> 再调用分类的load
a) 先编译的分类,优先调用load

2.initialize
1> 先初始化父类
2> 再初始化子类(可能最终调用的是父类的initialize方法)


objc_associationPolicy

  • 常用方法
static void *MyKey = &MyKey;
objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, MyKey)

static char MyKey;
objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, &MyKey)

使用属性名作为key
objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(obj, @"property");

使用get方法的@selecor作为key
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))
- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)name
{
    // 隐式参数
    // _cmd == @selector(name)
    return objc_getAssociatedObject(self, _cmd);
}

- (void)setWeight:(int)weight
{
    objc_setAssociatedObject(self, @selector(weight), @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (int)weight
{
    // _cmd == @selector(weight)
    return [objc_getAssociatedObject(self, _cmd) intValue];
}

- (NSString *)name
{
    return objc_getAssociatedObject(self, @selector(name));
}
- (int)weight
{
    return [objc_getAssociatedObject(self, @selector(weight)) intValue];
}

#define MJNameKey @"name"
#define MJWeightKey @"weight"
- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)name
{
    return objc_getAssociatedObject(self, MJNameKey);
}

- (void)setWeight:(int)weight
{
    objc_setAssociatedObject(self, MJWeightKey, @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (int)weight
{
    return [objc_getAssociatedObject(self, MJWeightKey) intValue];
}

static const void *MJNameKey = &MJNameKey;
static const void *MJWeightKey = &MJWeightKey;
- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)name
{
    return objc_getAssociatedObject(self, MJNameKey);
}

- (void)setWeight:(int)weight
{
    objc_setAssociatedObject(self, MJWeightKey, @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (int)weight
{
    return [objc_getAssociatedObject(self, MJWeightKey) intValue];
}

static const char MJNameKey;
static const char MJWeightKey;
- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, &MJNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)name
{
    return objc_getAssociatedObject(self, &MJNameKey);
}

- (void)setWeight:(int)weight
{
    objc_setAssociatedObject(self, &MJWeightKey, @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (int)weight
{
    return [objc_getAssociatedObject(self, &MJWeightKey) intValue];
}
KVC 、KVO、 Category、objc_associationPolicy_第3张图片
image.png
  • 关联对象并不是存储在被关联对象本身内存中
  • 关联对象存储在全局统一的一个AssociationManage中
  • 设置关联对象为nil,就相当于移除关联对象


    KVC 、KVO、 Category、objc_associationPolicy_第4张图片
    image.png

你可能感兴趣的:(KVC 、KVO、 Category、objc_associationPolicy)