KVC 、KVO、 Category、objc_associat
2019-06-15 本文已影响0人
小的小碰撞
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:的原理
image.png -
valueForKey:的原理
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];
}
image.png
- 关联对象并不是存储在被关联对象本身内存中
- 关联对象存储在全局统一的一个AssociationManage中
-
设置关联对象为nil,就相当于移除关联对象
image.png