乐虎游戏|乐虎国际登录|欢迎你

iOS 用RunTime重写KVO<附德姆o>

日期:2020-03-29编辑作者:计算机资讯

3.在第二步之后,我点击一个button ,push 到 另外一个ViewController(TestViewController)里面,然后在TestViewController里面,点击button ,在这个button 的点击事件里面去执行下面的代码:

KVO相信iOS开发者们都听说过,在面试中也会被常常问到,但是呢对于KVO来说更多的事情是由系统来做的,依赖于运行时,相对于Notification,delegate来说是比较简单的,提供观察属性旧值与新值,以下单纯的说下自己对KVO的实现原理粗略理解,用RunTime重写一下KVO,有理解不恰当的地方,请提出,谢谢大家

利用Runtime 实现简单的自定义kvo 

代码github github.com/zswj/custom-KVO


系统kvo实现原理:主要原理是:创建个被监听对象的子类,然后重新被监听属性的set方法,当这个属性被修改的时候,就让监听者调用某个方法

实现:创建个继承与NSObject的Person 并添加个age属性

为NSObject添加个分类 并添加一个监听方法

- (void)ZS_addObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options context:(nullablevoid*)context;

添加kvo监听

Person*p = [[Personalloc]init];

p.age=18;

_p= p;

//自定义KVO

[pZS_addObserver:selfforKeyPath:@"age"options:NSKeyValueObservingOptionNewcontext:nil];

NSObject分类中实现

这个方法 

- (void)ZS_addObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options context:(nullablevoid*)context {

//动态建个子类!重写监听属性的set方法

/*

1.自定义一个类继承与self

2.重写父类的被监听的属性的set方法(age)

3.调用obsetver的observeValueForKeyPath方法

*/

//动态添加一个类

NSString* oldClassN =NSStringFromClass([selfclass]);

NSString* newClassN = [@"zs_"stringByAppendingString:oldClassN];

constchar*name = [newClassNUTF8String];

//创建Person的子类

Class MyClass =objc_allocateClassPair([selfclass], name,0);

//重写setAge方法

class_addMethod(MyClass,@selector(setAge:), (IMP)setAge,"v@:i");

//注册一个类(注册这个子类)

objc_registerClassPair(MyClass);

//修改被观察对象的isa指针也就是self的isa指针此刻self就是继承与Person的子类对象

object_setClass(self, MyClass);

//将观察者属性保存到当前累里面(被监听的值改变的时候,可取出执行方法)

objc_setAssociatedObject(self, (__bridgeconstvoid*)@"objc", observer,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

//相当于重写setAge方法实现

voidsetAge(idself,SEL_cmd,intage) {

/*

要调用super的setAge方法

让这个类指针指向父类调用setAge方法后在让这个类指向子类

*/

//保存当前类

Class myClass = [selfclass];

NSLog(@"调用了没有%@",self);

//将self的isa指针改成父类调用父类的setAge方法

object_setClass(self,class_getSuperclass([selfclass]));

//调用父类

objc_msgSend(self,@selector(setAge:), age);

//通知观察者

//拿出观察者

idobjc =objc_getAssociatedObject(self, (__bridgeconstvoid*)@"objc");

//通知观察者执行方法

objc_msgSend(objc,@selector(observeValueForKeyPath:ofObject:change:context:),self,@"age",nil,nil);

//再把当前类改成子类

object_setClass(self, myClass);

}

for (id objcin array) {

a.首先说一下使用场景:

第二步 实现方案思路

1.给目标对象添加观察者

《方案二》 方案二利用 模型数组 进行存储记录第一步 利用交换方法,拦截到需要的东西

3.移除

return YES;

图片 1

}

2.实现步骤

动态类名

NSString * oldClassName = NSStringFromClass([self class]);

NSString * newName = [@"NSMBXB_" stringByAppendingString:oldClassName];

const char * newClassName = [newName UTF8String];

Class MyClass = objc_allocateClassPair([self class], newClassName, 0);

class_addMethod(MyClass, @selector, setName, "v@:@");

objc_registerClassPair;

object_setClass(self, MyClass);

objc_setAssociatedObject(self, (__bridge const void *)@"123", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

- observerKeyPath:(NSString *)key

- removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

[self removeDasen:observerforKeyPath:keyPath];

1.在我们自己定义的仿KVO方法中来写

示例代码:

- MBXB_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:context{

//搞事

}

@implementation DSObserver

3.实现setName重写方法

void setName(id self,SEL _cmd,NSString * newName){

//搞事情

}

id class = [self class];

object_setClass(self, class_getSuperclass([self class]));

objc_msgSend(self, @selector,newName);

id observer = objc_getAssociatedObject(self, (__bridge const void *)@"123");

objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),self,@"name",@{@"new":newName},nil);

object_setClass(self, class);

此时我们用runtime重写KVO基本简单实现了,那么我们接下来实验一下

示例代码:

//添加

Dog * dog = [[Dog alloc]init];

[dog MBXB_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

_dog = dog;

//实现

- observeValueForKeyPath:(NSString *)keyPath ofObject:object change:(NSDictionary*)change context:context{

NSLog(@"%@===>YES成功了%@",change,_dog.name);

}

//点击屏幕来测试:

- touchesBegan:touches withEvent:(UIEvent *)event {

static int i = 0;

i++;

_dog.name = [NSString stringWithFormat:@"%d",i];

}

点击屏幕测试结果,是不是成功了呢??

图片 2

最后为大家奉献上本文的代码连接,大家多多点赞哦demo,希望大家下载star

* kvo

2.处理变更通知

然后我再去释放 复写系统 dealloc 这个方法

一个目标对象管理所有依赖于目标对象的观察者,在自身状态改变的时候主动通知观察者->能够监听某个对象属性值的改变<1对多>

}

KVO--键-值观察(observing)---->设计模式中的观察者模式;

}

当KVO 监听到目标对象属性值改变后,就会调用这个方法,change这个字典保存了变更信息,具体是哪些信息取决于注册时的NSKeyValueObservingOptions

github地址:点击打开链接

某个类被第一次观察的时,系统会在运行期动态的创建一个该类的子类,然后在子类中重写被观察属性的setter方法,在子类中的重写的setter方法中实现真正的通知机制,子类除了重写setter方法外还重写了class方法其欺骗调用者子类就是原本的父类(内部其实是将父类的isa指针指向子类),父类就成为了派生类的对象,所以该对象对setter方法的调用,就会调用被重写子类的setter方法,激活通知机制,除此之外子类还重写了dealloc方法来释放资源

dispatch_once(&onceToken, ^{

ge:(NSDictionary *)change context:context;

//方案一:利用 @try @catch(只能针对删除多次KVO的情况下)

- observeValueForKeyPath:(NSString *)keyPath ofObject:object chan

在dealloc方法里面执行下面代码(我只是举个例子,监听的对象不一样,具体代码也不一样)

[dog addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

/** * 方案一 :利用 @try @catch(只能针对删除多次KVO的情况下)

代码示例:

Method systemAddMethod = class_getClassMethod([self class],addSel);

return nil;

NSString *keyPath = [Properties valueForKeyPath:@"_keyPath"];

Method DasenAddMethod = class_getClassMethod([self class], myaddSel);

#import@interface ObserverData :NSObject

当这个方法执行完之后,就会出现前面所展示的错误

@property (nonatomic,copy) NSString *keyPath;

《方案一》

4,NSKeyValueObservance属性简单说明

6,observationInfo结构 (箭头所指是我们等下需要用到的地方)

context:context // 添加监听时传来的值 {

} @catch (NSException *exception) {

—————————————————————————————————————————————————————

}

下面我讲给大家讲解几个解决的方法(百度查资料的,亲自验证,安全可靠),

参考人员:tyh

NSMutableArray *Observers = [DSObserversharedDSObserver];

@property (nonatomic,strong)id objc;

《方案三》

// 交换后的方法

- removeDasen:(NSObject *)observer forKeyPath:(NSString *)keyPath

/*Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observerfor the key path "kvoState" frombecause it is not registered as an observer.'*/

上述两个方法的代码同案例1 的一样(同样是新建一个类目NSObject+DSKVO),然后在写下面方法#pragma mark - 第二种方案,利用私有属性// 交换后的方法

(NSKeyValueObservingOptions)options context:context{

2.我在我创建的一个ViewController(SecondViewController)里面去监听这个属性

{

那么,那个类目里面的代码是这样的:(导入头文件:#import)

@try {

}

[selfswitchMethod];

@end

if ([data.objcisEqual:self] && [data.keyPathisEqualToString:keyPath]) {

}

AppDelegate *appDelegate = (AppDelegate *)[UIApplicationsharedApplication].delegate;

}

}

if ([keyPath isEqualToString:@"kvoState"]) {

}

self.objc = objc;

method_exchangeImplementations(systemRemoveMethod, DasenRemoveMethod);

});

NSMutableArray *Observers = [DSObserversharedDSObserver];

[selfswitchMethod];}+ switchMethod{

方案有三种:/** * 那么iOS开发-黑科技防止多次添加删除KVO出现的问题

change:(NSDictionary *)change // 属性的值

}这种情况还需要新建几个文件:DSObserver 、ObserverData

[selfaddDasen:observer forKeyPath:keyPath options:options context:context];

return self;

}

AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

上述就是方案二了

}

return;

第三步 存储之前的检索处理1,在存储之前,为了防止多次addObserver相同的属性,这个时候我们就可以,遍历数组,取出每个一个模型,然后取出模型中的对象,首先判断对象是否一致,然后判断keypath是否一致

-buttonAction{

SEL removeSel = @selector(removeObserver:forKeyPath:);

objc = [NSMutableArrayarray];

Method systemAddMethod = class_getClassMethod([self class],addSel);

@end#import "ObserverData.h"

static id objc;

#pragma mark - 第一种方案

5,拿出keyPath

1,是在监听哪个对象。

3,通过Dump Foundation.framework 的头文件,和直接xcode查看observationInfo的结构,发现有一个数组用来存储NSKeyValueObservance对象,经过测试和调试,发现这个数组存储的需要监听的对象中,监听了几个属性,如果监听两个,数组中就是2个对象。

[Observers removeObject:userPathData];

本文由乐虎游戏发布于计算机资讯,转载请注明出处:iOS 用RunTime重写KVO&lt;附德姆o&gt;

关键词:

乐虎国际登录四十二十四线程与NSTimer

NSInteger转 Byte 数组,长度为2 1.Ios主线程,也称UI线程,在主线程中使用NSTimer,runloop是自动开启的,(如果NSTimer当前所...

详细>>

【LX豪彩】UIProgressView进程条的性格介绍

- viewDidLoad { - (void)viewDidLoad { [super viewDidLoad]; //实例化一个进度条,有两种样式,一种是UIProgressViewStyleBar一种是UIPro...

详细>>

浅谈常见的Loading进度条动效形式

在浏览网页、玩游戏、手提式有线电话机接受等气象,因为网速慢或是硬件差的关联,难免会遇上等候加载的场所,...

详细>>

iOS动漫之自定义转场动漫(push卡塔尔国

push.gif iOS7 开始苹果推出了自定义转场的 API 。从此,任何可以用 CoreAnimation实现的动画,都可以出现在两个 ViewContr...

详细>>