• iOS


    1、KVO

    • KVO 是 Key-Value Observing 的简写,是键值观察的意思,属于 runtime 方法。Key Value Observing 顾名思义就是一种 observer 模式用于监听属性变量值的变化,也是运行时的方法,当实例变量改变时,系统会自动采取一些动作。KVO 跟 NSNotification 有很多相似的地方,用 addObserver:forKeyPath:options:context: 去 start observer, 用 removeObserver:forKeyPath:context 去 stop observer, 回调就是 observeValueForKeyPath:ofObject:change:context:。

    • 对于 KVO 来说,我们要做的只是简单 update 我们的 property 的数据,不需要像 NSNotificationCenter 那样关心是否有人在监听你的请求,如果没有人监听该怎么办,所有 addObserver, removeObserver, callback 都是想要监听的你的 property 的 class 做的事情。曾经做个项目,用 NSNotificationCenter post Notification 在一个 network callback 里面,可是这时候因为最早的 addObserver 的 class 被释放了,接着生成的 addObserver 的 class, 就接受到了上一个 observer 该监听的事件,所以造成了错误,那时候的解决方案是为 addObserve key 做 unique,不会 2 次 addObserver 的 key 是相同的,但是有了 KVO, 我们同样可以用 KVO 来完成,当 addOberver 的的 object remove 的时候,就不会有这样的 callback 被调用了。

    • KVO 给我们提供了更少的代码,和 NSNotification 比好处,不需要修改被观察的 class, 永远都是观察你的人做事情。 但是 KVO 也有些毛病:

      • 1、如果没有 observer 监听 keyPath, removeObsever:forKeyPath:context: 这个 keyPath, 就会 crash(崩溃), 不像 NSNotificationCenter removeObserver。
      • 2、对代码你很难发现谁监听你的 property 的改动,查找起来比较麻烦。
      • 3、对于一个复杂和相关性很高的 class,最好还是不要用 KVO, 就用 delegate 或者 notification 的方式比较简洁。
    • KVO 使用分三步:

      • 1、注册成为观察者。
      • 2、观察者定义 KVO 的回调。
      • 3、移除观察者。
    • KVO 使用注意:

      • KVO 是同步的,一旦对象的属性发生变化,只有用同步的方式,才能保证所有观察者的方法能够执行完成。KVO 监听方法中,不要有太耗时的操作。

      • KVO 的方法调用,是在对应的线程中执行的。在子线程修改观察属性时,观察者回调方法将在子线程中执行。

      • 在多个线程同时修改一个观察属性的时候,KVO 监听方法中会存在资源抢夺的问题,需要使用互斥锁。如果涉及到多线程,KVO 要特别小心,通常 KVO 只是做一些简单的观察和处理,千万不要搞复杂了,KVO的监听代码,一定要简单。

      • 一定要删除观察者,如果不删除观察者,释放对象,会直接崩溃。An instance 0x7fd340ebc400 of class KvoClass was deallocated while key value observers were still registered with it.

    • 在 Swift 中使用 KVO 的前提条件:

      • 1、观察者和被观察者都必须是 NSObject 的子类,因为 OC 中 KVO 的实现基于 KVC 和 runtime 机制,只有是 NSObject 的子类才能利用这些特性;
      • 2、观察的属性需要使用 dynamic 关键字修饰,表示该属性的存取都由 runtime 在运行时来决定,由于 Swift 基于效率的考量默认禁止了动态派发机制,因此要加上该修饰符来开启动态派发。

    2、KVO 的使用

    • Objective-C

      	// KvoClass.h
      
      		@interface KvoClass : NSObject
      
      		@property(nonatomic, copy) NSString *name;
      
      		@end
      
      	// ViewController.m
      
      		@property(nonatomic, retain) KvoClass *kvoObject;
      
      		_kvoObject = [[KvoClass alloc] init];
      
    • Swift

      	// KvoClass.swift
      
      		class KvoClass: NSObject {
      
      			dynamic var name:String!
      		}
      
      	// ViewController.swift
      
      		var nameContext = "nameChange"
      
      		var kvoObject = KvoClass()
      

    2.1 KVO 添加

       	- (void)addObserver:(NSObject *)observer 
       	         forKeyPath:(NSString *)keyPath 
       	            options:(NSKeyValueObservingOptions)options 
       	            context:(nullable void *)context;
       	            
    	public func addObserver(observer: NSObject, 
    	              forKeyPath keyPath: String, 
    	                         options: NSKeyValueObservingOptions, 
    	                         context: UnsafeMutablePointer<Void>)
    
       	参数说明:
           第一个参数 observer 是观察的类;
           第二个参数 keyPath 是被观察的类中被观察的属性;
           第三个参数 options 是观察选项;
           第四个参数 context 是传递的上下文内容。
    
    • Objective-C

      	// 添加观察者
      	[_kvoObject addObserver:self 
      	             forKeyPath:@"name" 
      	                options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld 
      	                context:@"nameChange"];
      
      	// 改变被观察的键对应的值
      	_kvoObject.name = @"xiao bai";
      	sleep(2);
      	_kvoObject.name = @"xiao hei";
      
    • Swift

      	// 添加观察者
      	kvoObject.addObserver(self, forKeyPath:"name", options:[.New, .Old], context:&nameContext)
      
      	// 改变被观察的键对应的值
      	kvoObject.name = "xiao bai"
      	sleep(2)
      	kvoObject.name = "xiao hei"
      

    2.2 KVO 回调

    	- (void)observeValueForKeyPath:(nullable NSString *)keyPath 
    	                      ofObject:(nullable id)object 
    	                        change:(nullable NSDictionary<NSString*, id> *)change 
    	                       context:(nullable void *)context;
    	
    	public func observeValueForKeyPath(keyPath: String?, 
    	                           ofObject object: AnyObject?, 
    	                                    change: [String : AnyObject]?, 
    	                                   context: UnsafeMutablePointer<Void>)
    	
    	参数说明:
        	keyPath:监控的 key;
        	object:被监控的对象的基本属性;
        	change:被监控的对象的 key 对应的 value 值的变化(kind:类型,new:变化后的值,old:变化前的值。
    
    • Objective-C

      	// 系统自带方法
      	- (void)observeValueForKeyPath:(NSString *)keyPath 
      	                      ofObject:(id)object 
      	                        change:(NSDictionary *)change 
      	                       context:(void *)context {
      
          	if (context == @"nameChange") {
      
      			NSLog(@"name 值被改变 kind = %@, oldValue = %@, newValue = %@", 
      			             change[@"kind"], change[@"old"], change[@"new"]);
      
          	} else {
          		[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
           	}
      	}
      
    • Swift

      	override func observeValueForKeyPath(keyPath: String?, 
      	                             ofObject object: AnyObject?, 
      	                                      change: [String : AnyObject]?, 
      	                                     context: UnsafeMutablePointer<Void>) {
          
          	if context == &nameContext {
              
              	print("name 值被改变 kind = (change![NSKeyValueChangeKindKey]), 
              	                 oldValue = (change![NSKeyValueChangeOldKey]), 
              	                 newValue = (change![NSKeyValueChangeNewKey])")
          	}
          	else {
              	super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
          	}
      	}
      

    2.3 KVO 移除

    • 在实际工作中需要在合适的时候移除观察者身份。

      	NS_AVAILABLE(10_7, 5_0)
      	- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context;
      	- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
      
      	public func removeObserver(observer: NSObject, forKeyPath keyPath: String, context: UnsafeMutablePointer<Void>)
      	public func removeObserver(observer: NSObject, forKeyPath keyPath: String)
      
      	参数说明:
          	第一个参数 observer 是观察的类;
          	第二个参数 keyPath 是被观察的类中被观察的属性;
          	第三个参数 context 是传递的上下文内容。
      
    • Objective-C

      	- (void)dealloc {
              
          	// 移除观察者
        		[_kvoObject removeObserver:self forKeyPath:@"name" context:@"nameChange"];
      	}
      
    • Swift

      	deinit {
          
          	// 移除观察者
          	kvoObject.removeObserver(self, forKeyPath:"name", context:&nameContext)
      	}
      
  • 相关阅读:
    TC2.0库函数大全
    【转帖】2004年度电影作品盘点之好莱坞篇
    C++文件流读写txt文件
    Linux 使用技巧
    最长字符串匹配算法(KMP算法)
    回调函数
    C#中对XML文件的一些基本操作[转载]
    指针与引用的区别
    中文核心期刊目录
    十大最考验演技的角色
  • 原文地址:https://www.cnblogs.com/QianChia/p/5771074.html
Copyright © 2020-2023  润新知