- 动态创建一个子类,注册
- 修改被观察者的类型,修改isa指针
- 添加set方法
- 动态绑定属性
#import <objc/runtime.h>
@implementation NSObject (MGKVO)
- (void)MG_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context
{
NSString *oldClassName = NSStringFromClass([self class]);;
NSString *newClassNmae = [@"MGKVO_" stringByAppendingString:oldClassName];
const char *newName = newClassNmae.UTF8String;
// 动态创建类
Class MyClass = objc_allocateClassPair([self class], newName, 0);
// 注册这个类
objc_registerClassPair(MyClass);
// 修改调用者的类型
object_setClass(self, MyClass);
// 重写set方法
class_addMethod(MyClass, @selector(setName:), (IMP)setName, "v@:");
// --- 保存观察者
objc_setAssociatedObject(self, @"observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// 把隐含参数补齐,就可以接收name了
// id self, SEL _cmd
void setName(id self, SEL _cmd, NSString *name)
{
NSLog(@"setName---调用了----%@", name);
// 消息机制
// 保存当前类 MGKVO_Person
Class MyClass = [self class];
// 将self指向父类,准备调用父类方法
object_setClass(self, class_getSuperclass([self class]));
// 调用父类方法
objc_msgSend(self, @selector(setName:), name);
// 通知观察者
id observer = objc_getAssociatedObject(self, @"observer");
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:), @"name", self, nil, nil);
// 改回子类
object_setClass(self, MyClass);
}
@end
使用
- (void)viewDidLoad {
[super viewDidLoad];
MGPerson *p = [[MGPerson alloc] init];
p.name = @"lisa";
_p = p;
// [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
[p MG_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
}
// 监听方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@监听到%@属性的改变为%@", object, keyPath, change);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSString *randomStr = [NSString stringWithFormat:@"%d", arc4random()%10];
self.p.name = randomStr;
NSLog(@"name的值------%@", self.p.name);
}
- 类:方法调用
- 对象:成员变量
- 对象没有变化(内存地址),改变他的Class类型
- 子类没有父类方法,只是在子类中找不到方法时才到父类里去找
- 可以调用
- 子类中没有,去父类找
- 子类重写父类方法(直接给父类发消息的结构体较复杂)
- OC里2个隐藏参数!重写子类方法时,要补齐
- id类型的self,SEL类型的_cmd
objc_msgSend(class_getSuperclass([self class]), @selector(setName:), name);
// Use of undeclared identifier 'super'
// objc_msgSendSuper(super, @selector(setName:), name);
self 是子类
一般用message,底层都会用到消息机制
改回来,不然观察不到了
调用父类安全,保证父类原有的行为
直接消息转发,查找
- 而
category
是无法添加实例变量的(因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的) -
extension
一般用来隐藏类的私有信息 - 对class进行伪装,利用
getClass
拿不到真实的类型
网友评论