KVO 以及KVC的内容, 这些内容有什么?
KVO 【key value observing】 键值监听 —— 用于监听某个对象属性值的改变。
先来看一个例子
代码段:
self.person = [XNPerson new];
self.person2 = [XNPerson new];
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
执行之后的结果
PS : 可以看到person 的class已经发生了改变【person 增加了kvo的监听】, 而person2的没有变化。
KVO的基本原理:
- 从上面,我们可以知道, kvo的实现修改了原来类的class类型[也就是修改isa指针],就是查找方法是从新的类对象去查找。
- 调用super的setter的方法,以及通知observevalue的通知方法, 当然这里还是可以实现通过willchangeKey: 那个也是可以处理的;
自己去写一个有关的例子:
@interface XNPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end
#import "NSObject+Category.h"
#import <objc/runtime.h>
#import <objc/message.h>
static NSString *const LYKVO_ = @"LYKVO_notify_"; //专门做KVO的内容
有关KVO的具体实现内容
@implementation NSObject (Category)
// 1) 调用super的setter方法
// 2) 通知观察者
void setName(id self, SEL _cmd, NSString *param) {
// objc_msgSendSuper(self, _cmd, param);
// 保存子类类型
id class = [self class];
// 改变self的isa指针
object_setClass(self, class_getSuperclass(class));
objc_msgSend(self, @selector(setName:), param);
id objc = objc_getAssociatedObject(self, "objc");
NSMutableDictionary *change = [NSMutableDictionary new];
[change addEntriesFromDictionary:@{@(NSKeyValueObservingOptionOld):[self performSelector:@selector(name)]}];
[change addEntriesFromDictionary:@{@(NSKeyValueObservingOptionNew):param}];
objc_msgSend(objc, @selector(observeValueForKeyPath:ofObject:change:context:), @"name", self,change,nil);
object_setClass(self, class);
}
- (void)ly_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
{
if (keyPath.length <=0) return;
NSString *superClsName = NSStringFromClass([self class]);
const char * newClassName = [[@"LYKVO_" stringByAppendingString:superClsName] UTF8String];
Class newCls = objc_allocateClassPair([self class], newClassName, 0);
objc_registerClassPair(newCls);
class_addMethod(newCls, @selector(setName:), (IMP)setName, "v@:@");
object_setClass(self, newCls);
objc_setAssociatedObject(self, "objc", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
PS: 通过集成子类来动态实现对应的内容,这样的方式也可以使用到修改按钮的点击范围, 或者说控件的点击范围等等操作。
按钮点击范围扩展的例子:
- (UIEdgeInsets)wk_expandEdgeInsets {
NSValue * value = objc_getAssociatedObject(self, @selector(wk_expandEdgeInsets));
return value ? [value UIEdgeInsetsValue] : UIEdgeInsetsZero;
}
- (void)setWk_expandEdgeInsets:(UIEdgeInsets)wk_expandEdgeInsets {
if (![NSStringFromClass(self.class) containsString:@"WKClickArearExpand_"]) {
NSString *className = [NSString stringWithFormat:@"WKClickArearExpand_%@",self.class];
Class kclass = objc_getClass([className UTF8String]);
if (!kclass)
{
kclass = objc_allocateClassPair([self class], [className UTF8String], 0);
SEL setterSelector = NSSelectorFromString(@"pointInside:withEvent:");
Method setterMethod = class_getInstanceMethod([self class], setterSelector);
const char *types = method_getTypeEncoding(setterMethod);
class_addMethod(kclass, setterSelector, (IMP)WKClickArearExpand_pointInside, types);
objc_registerClassPair(kclass);
}
object_setClass(self, kclass);
}
objc_setAssociatedObject(self, @selector(wk_expandEdgeInsets), [NSValue valueWithUIEdgeInsets:wk_expandEdgeInsets], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
static BOOL WKClickArearExpand_pointInside(id self, SEL _cmd, CGPoint point, UIEvent *event) {
struct objc_super superclass = {
.receiver = self,
.super_class = class_getSuperclass(object_getClass(self))
};
BOOL (*objc_msgSendSuperCasted)(const void *, SEL,CGPoint, UIEvent*) = (void *)objc_msgSendSuper;
UIEdgeInsets outerEdge = UIEdgeInsetsZero;
// 这里判断的是有关的范围内容 , 修改点击的范围,我们修改对应的hittest的点的坐标
if ([self isKindOfClass:[UIButton class]]) {
UIButton * btn = (UIButton *)self;
outerEdge = btn.wk_expandEdgeInsets;
{// fix x
BOOL needFixX = NO;
CGFloat curX = 0.f;
if ((point.x <0 &&((point.x +outerEdge.left)>=0))) {
curX = point.x + outerEdge.left;
needFixX = YES;
}
else if ((point.x >btn.width) && (point.x <= (btn.width +outerEdge.right))) {
curX = point.x;
needFixX = YES;
}
else {}
if (needFixX) {
CGFloat width = btn.width;
if (width!=0) {
curX = (NSInteger)curX % (NSInteger)width;
}
point.x = curX;
}
}
{ // fix y
BOOL needFixY = NO;
CGFloat curY = 0.f;
if ((point.y <0 &&((point.y +outerEdge.top)>=0))) {
curY = point.y + outerEdge.top;
needFixY = YES;
}
else if ((point.y >btn.height) && (point.y <= (btn.height +outerEdge.bottom))) {
curY = point.y;
needFixY = YES;
}
else {}
if (needFixY) {
CGFloat height = btn.height;
if (height!=0) {
curY = (NSInteger)curY % (NSInteger)height;
point.y = curY;
}
}
}
}
return objc_msgSendSuperCasted(&superclass, _cmd, point, event);
}
创建动态类以及添加方法
有关的参考内容:
参考
https://www.jianshu.com/writer#/notebooks/50287346/notes/88274270/preview
网友评论