ios开发iOS学习笔记程序员

iOS 观察者KVO

2016-08-11  本文已影响368人  生如夏花逝如秋叶
KVO 的基本概念(Key Value Observing)
person.name = @"xiao wang";改变姓名 person.name = @"xiao ming";
  /*
     作用:给对象绑定一个观察者(监听者)
     addObserver:   观察者
     forKeyPath:    要监听的属性
     options:       选项(方法中拿到属性值)
     context:       上下文一般为nil
   */
  //监听变更旧的值
  [person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld context:nil];
此时打印:
change = {
  kind = 1;
  old = "xiao wang";
}
  //监听变更新的值
  [person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
此时打印:
change = {
  kind = 1;
  new = "xiao ming";
}
  //同时监听变更旧的值和新的值
  [person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
此时打印:
change = {
  kind = 1;
  new = "xiao ming";
  old = "xiao wang";
}
/**
*  当监听的属性值发生改变是执行
*
*  @param keyPath 发生改变的属性
*  @param object  改变的属性所属的对象
*  @param change  改变的内容
*  @param context 上下文
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
  
  // 打印改变的内容
  NSLog(@"change = %@",change);
}
打印结果如:
change = {
  kind = 1;
  new = "xiao ming";
  old = "xiao wang";
}

KVO 使用需要注意的一些地方
- (void)viewDidLoad {
  [super viewDidLoad];
  
  Person *person = [[Person alloc] init];
  
  person.name = @"xiao wang";
  
  //监听变更新的值
  [person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
  
  person.name = @"xiao ming";
}
/**
*  当监听的属性值发生改变是执行
*
*  @param keyPath 发生改变的属性
*  @param object  改变的属性所属的对象
*  @param change  改变的内容
*  @param context 上下文
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
  
  // 打印改变的内容
  NSLog(@"change = %@",change);
}
  
在执行完viewDidLoad方法后,person会被销毁。然而viewDidLoad在person不需要再使用的时候并没有移除person的观察者身份会引起crash
change = {
  kind = 1;
  new = "xiao ming";
}
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x7fa633d97950 of class Person was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x7fa633d920d0> (
<NSKeyValueObservance 0x7fa633d9ad80: Observer: 0x7fa633e13090, Key path: name, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x7fa633d9ac40>
)'
*** First throw call stack:
(
  0   CoreFoundation                      0x000000010ca92e65 __exceptionPreprocess + 165
  1   libobjc.A.dylib                     0x000000010c50bdeb objc_exception_throw + 48
  2   CoreFoundation                      0x000000010ca92d9d +[NSException raise:format:] + 205
  3   Foundation                          0x000000010c11d611 NSKVODeallocate + 294
  4   libobjc.A.dylib                     0x000000010c51fafe _ZN11objc_object17sidetable_releaseEb + 232
  5   图片处理                        0x000000010c0089bc -[ViewController viewDidLoad] + 252
  6   UIKit                               0x000000010cfd5f98 -[UIViewController loadViewIfRequired] + 1198
  7   UIKit                               0x000000010cfd62e7 -[UIViewController view] + 27
  8   UIKit                               0x000000010ceacab0 -[UIWindow addRootViewControllerViewIfPossible] + 61
  9   UIKit                               0x000000010cead199 -[UIWindow _setHidden:forced:] + 282
  10  UIKit                               0x000000010cebec2e -[UIWindow makeKeyAndVisible] + 42
  11  UIKit                               0x000000010ce37663 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4131
  12  UIKit                               0x000000010ce3dcc6 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1760
  13  UIKit                               0x000000010ce3ae7b -[UIApplication workspaceDidEndTransaction:] + 188
  14  FrontBoardServices                  0x000000010f80b754 -[FBSSerialQueue _performNext] + 192
  15  FrontBoardServices                  0x000000010f80bac2 -[FBSSerialQueue _performNextFromRunLoopSource] + 45
  16  CoreFoundation                      0x000000010c9bea31 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
  17  CoreFoundation                      0x000000010c9b495c __CFRunLoopDoSources0 + 556
  18  CoreFoundation                      0x000000010c9b3e13 __CFRunLoopRun + 867
  19  CoreFoundation                      0x000000010c9b3828 CFRunLoopRunSpecific + 488
  20  UIKit                               0x000000010ce3a7cd -[UIApplication _run] + 402
  21  UIKit                               0x000000010ce3f610 UIApplicationMain + 171
  22  图片处理                        0x000000010c00a51f main + 111
  23  libdyld.dylib                       0x000000010f1ce92d start + 1
  24  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
  
修复viewDidLoad:
- (void)viewDidLoad {
  [super viewDidLoad];
  
  Person *person = [[Person alloc] init];
  
  person.name = @"xiao wang";
  
  //监听变更新的值
  [person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
  
  person.name = @"xiao ming";
  
  //移除观察者身份
  [person removeObserver:self forKeyPath:@"name"];

}

#import <Foundation/Foundation.h>
    
@interface Student : NSObject
    
// 离考试时间
@property (nonatomic, assign) int examTime;
    
@end

Student.m文件

#import "Student.h"
  
@implementation Student
  
/**
*  初始化方法
*
*  @return <#return value description#>
*/
- (instancetype)init{
    
  self = [super init];
    
  if (self != nil) {
        
      self.examTime = 10;
        
      //设置一个定时器,减少离考试的时间examTime
      [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeGone:) userInfo:nil repeats:YES];
        
  };
    
  return self;
}
  
//定时器方法
- (void)timeGone:(NSTimer *)timer{
    
  self.examTime--;
  /*
   self.examTime--;
   在ViewController 的viewDidLoad 方法中监听
   Student *student = [[Student alloc] init];
     
   [student addObserver:self forKeyPath:@"examTime" options:NSKeyValueObservingOptionOld context:nil];
     
   是可以监听的到时间变化的
   */
    
  //_examTime--;
  /*
   在ViewController 的viewDidLoad 方法中监听
   Student *student = [[Student alloc] init];
     
   [student addObserver:self forKeyPath:@"examTime" options:NSKeyValueObservingOptionOld context:nil];
     
   是不能监听的到时间变化的
   */
    
    
   //[self setValue:[NSNumber numberWithInt:_examTime] forKey:@"examTime"];
  /*
   在ViewController 的viewDidLoad 方法中监听
   Student *student = [[Student alloc] init];
     
   [student addObserver:self forKeyPath:@"examTime" options:NSKeyValueObservingOptionOld context:nil];
     
   是可以监听的到时间变化的
   */
    
    
   //[self setValue:[NSNumber numberWithInt:_examTime] forKey:@"_examTime"];
  /*
   在ViewController 的viewDidLoad 方法中监听
   Student *student = [[Student alloc] init];
     
   [student addObserver:self forKeyPath:@"examTime" options:NSKeyValueObservingOptionOld context:nil];
   
   是不能监听的到时间变化的原因是KVO注册监听的key是@"examTime", KCV改变的key是@"_examTime"
   */
  
}
  
@end
    
在ViewController的viewDidLoad方法中使用
- (void)viewDidLoad {
  [super viewDidLoad];
    
  Student *student = [[Student alloc] init];
    
  [student addObserver:self forKeyPath:@"examTime" options:NSKeyValueObservingOptionOld context:nil];
    
  /*
   这里的代码存在问题
   没有添加移除student观察者身份,但此处只是为了验证_examTime--不能触发观察者模式
   固不作进一步优化
   */
}
  
/**
*  当监听的属性值发生改变是执行
*
*  @param keyPath 发生改变的属性
*  @param object  改变的属性所属的对象
*  @param change  改变的内容
*  @param context 上下文
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    
  // 打印改变的内容
  NSLog(@"change = %@",change);
    
}
  
打印结果:
change = {
  kind = 1;
  old = 10;
}
change = {
  kind = 1;
  old = 9;
}
change = {
  kind = 1;
  old = 8;
}
change = {
  kind = 1;
  old = 7;
}

上一篇 下一篇

猜你喜欢

热点阅读