iOS开发之通知中心、KVC、KVO

前言

本文的内容可能跟之前相比会比较长,主要因为讲了通知、KVO、KVC三个知识点,但我自认为条理还算清晰。建议学完一个敲一下,再进行下一个知识点的学习。

通知中心

  • What
    是一种一对多的信息广播机制,一个应用程序同时只能有一个NSNotificationCenter(通知中心)对象,因为如果有多个通知,发送通知的时候就不知道是该给谁发送了。
  • Where
    delegate和block也属于一种信息传递机制,但这两种都是一对一的,每次执行的方法都不一样,而通知是一对多,只要有地方触发通知,执行的是同一个方法。
  • How
    1. 添加一个通知

      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(paySalary) name:@"发工资啦" object:nil];
    2. 实现添加通知时方法选择器选择的方法

    3. 在需要发送通知的类中采用下面方法发送通知,发送成功便会执行步骤二实现的方法

      [[NSNotificationCenter defaultCenter] postNotificationName:@"发工资啦" object:nil];
    4. 移除通知,个人习惯在delloc中释放

      [[NSNotificationCenter defaultCenter] postNotificationName:nil object:nil userInfo:nil];
  • 代码示例
    1. 在viewController.m的viewDidLoad方法中注册一个通知

      注册通知
    2. 在viewController.m实现在注册通知时方法选择器选择的方法。


    3. 在NSNotificationViewController.m中的按钮点击事件方法中发送通知


      发送通知
    4. 在viewController.m中重写delloc方法,在方法移除通知。

      移除通知

KVC

  • What
    全称,key valued coding(键值编码)
  • Where
    最常用的是将字典数据转model
  • Why
    特殊情况下,更加简便,下文会进行分析。
  • How

    见代码

  • 代码示例

    该类中用到的Parent和Children类里面只有.h里面有东西,.m文件内什么都没。详情看最后附录。

//
//  KVCViewController.m
//  NSNotification、KVC、KVO
//
//  Created by GG on 16/1/20.
//  Copyright © 2016年 王立广. All rights reserved.
//

#import "KVCViewController.h"
#import "Parent.h"
@implementation KVCViewController

/*

- (void)viewDidLoad{

    [super viewDidLoad];

    self.title = @"KVC";

/*
 * 将字典@{@"name":@"红红",@"children":@{@"name":@"小红"}}采用多种方式转化为model.
 */

 NSDictionary *dict = @{@"name":@"红   红",@"children":@{@"name":@"小红"}};

#pragma mark -------------------老方式-------------------

//该类中用到的Parent和Children类里面只有.h里面有东西,.m文件内什么都没。详情看最后附录。
Parent *oldWayparent = [Parent new];

//给parent起名字
oldWayparent.name = dict[@"name"];

Children *children1 = [Children new];
children1.name = dict[@"children"][@"name"];

//给parent的孩子起名字
oldWayparent.children = children1;

NSLog(@"oldWay ======= %@的孩子叫做%@",oldWayparent.name,oldWayparent.children.name);


#pragma mark -------------------KVC 方式-------------------

/*

 * 采用kvc将字典转化为model的三种方式

 * 方式一 : 存值:[id setValue:<#value#> forKey:<#key#>];
           取值:[id valueForKey:<#key#>];

           给id对象的key属性赋值value。此处key的值一定必须要和在id对象key属性一模一样。

 * 方式二 : 存值:[id setValue:<#value#> forKeyPath:<#key.key#>];
           取值:[id valueForKeyPath:<#key#>];

           同样是给id对象的相应属性赋值,但此时后面将不再直接给出键,而是按照键值路径来查找出相应的键,系统会按『.』,自动进入对象内部,查找对象属性。坑①。

 * 方式三 : [id setValuesForKeysWithDictionary:<#NSDictionary#>];

           上面的两种方式都需要取出来字典中的值,赋值给对象的相应属性。如果该对象要是有八九十来个属性,就要写八九十来行代码。这样太麻烦。碰到这样情况直接采用方式三便可。直接将整个字典作为参数传进来,便可将字典转化为model对象。

 * 温馨提示 : 这三种方式并不是完全独立,不是不可混合使用的,要根据字典内容做决定,接下来我用上面的那个字典做一下简单分析。

 */

Parent *kvcWayParent = [Parent new];

//采用方式一给属性赋值,如果属性是用@property声明的可以直接用self.name = dict[@"name"],如果没有用@property,而是在大括号内声明的属性要用这种方式。
[kvcWayParent setValue:dict[@"name"] forKey:@"name"];

Children *children2 = [Children new];
[kvcWayParent setValue:children2 forKey:@"children"];

/*
 * 采用方式二给属性赋值。此时要注意以下两点:

 * 1、是forKeyPath,不是forkey
   2、forkeyPath后childeren一定必须要和kvcWayParent里面的children属性名字一样,它后面的name必须一定要和children里面的name属性名一样。
 */
[kvcWayParent setValue:dict[@"children"][@"name"] forKeyPath:@"children.name"];

NSLog(@"kvcWay ======= %@的孩子叫做%@",kvcWayParent.name,kvcWayParent.children.name);



Parent *newKvcWayParent = [Parent new];

/* 
 * 采用第三种方式将字典转化为model,此时我们要注意以下几点:

 * 1、字典里面有什么东西,挑出我们需要用的(一般都需要)在model类中给声明出来。例如字典里有name,我就要在model类中声明该属性。

 * 2、声明属性的时候要注意匹配数据类型。如果是数字,建议声明成NSNumber,因为在进行编码的时候,kvc会自动将字典中的数字转化为NSNumber类型。
 */


[newKvcWayParent setValuesForKeysWithDictionary:dict];
//坑②
NSLog(@"newsKvcWay ======= %@你太伟大了%@",newKvcWayParent.name,newKvcWayParent.children);

}

@end

坑①: 如果model类中还有其他自定义对象,在赋值之前一定要先给该自定义对象赋值,在给其属性赋值。如果直接按下面的方式赋值,是错误的。

Parent *testParent = [Parent new];
//给children.name赋值之前没有给children赋值
[testParent setValue:dict[@"children"][@"name"] forKeyPath:@"children.name"];

坑②:最后一句输出如下:

newsKvcWay ======= 红红你太伟大了 {
name = "\U5c0f\U7ea2";
}

出现这种情况的原因是我用一个Children的对象来接受字典里面的字典了。

KVO

  • What

    Key-Value Observing(键值观察),它提供一种机制,当指定的对象的属性被修改后(指的是属性的内存地址被修改),则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。

  • Where

    需要监听某对象某一属性的变化时

  • Why

    能够实时监听对象属性的变化

  • How
    1. 采用下面这个方法给属性添加观察者,各参数详情见下文

      - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
    2. 观察者实现下面方法,如果监听的属性发生变化,便会调用该方法。

      - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context;
    3. 适时调用下面方法移除观察者,个人习惯在delloc中释放。

      - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context;
  • 代码示例
     //
     //  KVOViewController.m
     //  NSNotification、KVC、KVO
     //
     //  Created by GG on 16/2/12.
     //  Copyright © 2016年 王立广. All rights reserved.
     //
    
     #import "KVOViewController.h"
     #import "Parent.h"
     @interface KVOViewController ()
     {
         Parent *parent;
    
         UILabel *label;
     }
     @end
    
     @implementation KVOViewController
    
     - (void)viewDidLoad {
         [super viewDidLoad];
    
         self.title = @"KVO";
    
         parent = [Parent new];
    
         Children *children = [Children new];
    
         parent.children = children;
    
         //给parent的起名字,给parent的children起名字
         parent.children.name = @"闪闪";
         parent.name = @"小闪";
    
         /*
          * 监听parent的children的名字
    
          * observer: 设置观察者
    
          * forkeyPath: 设置对象的属性,要注意这里传入的是字符串。在这里我传入的是"children.name"。也就是说我要监听的是parent对象的children属性的name属性。所以说KVO是在KVC的基础上实现的。
    
          * options: 
              NSKeyValueObservingOptionNew:当options中包括了这个参数的时候,观察者收到的change参数中就会包含NSKeyValueChangeNewKey和它对应的值,也就是说,观察者可以得知这个property在被改变之后的新值。
    
              NSKeyValueObservingOptionOld:和NSKeyValueObservingOptionNew的意思类似,当包含了这个参数的时候,观察者收到的change参数中就会包含NSKeyValueChangeOldKey和它对应的值。
    
              NSKeyValueObservingOptionInitial:当包含这个参数的时候,在addObserver的这个过程中,就会有一个notification被发送到观察者那里,反之则没有。
    
              NSKeyValueObservingOptionPrior:当包含这个参数的时候,在被观察的property的值改变前和改变后,系统各会给观察者发送一个change notification;在property的值改变之前发送的change notification中,change参数会包含NSKeyValueChangeNotificationIsPriorKey并且值为@YES,但不会包含NSKeyValueChangeNewKey和它对应的值。
    
              可以指定多个NSKeyValueObservingOptions,将他们用“或”连接后,作为options参数。常用的就前三种,第四种知道便可。目前我们只需知道前三种便可。
    
           * content: 可以将任意对象作为参数在这里传递。
    
          */
         [parent addObserver:self forKeyPath:@"children.name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionInitial context:nil];
    
         label = [[UILabel alloc]initWithFrame:CGRectMake(100, 200, kScreenWidth-200, 40)];
         label.text = [NSString stringWithFormat:@"%@的孩子名字叫做%@",parent.name,parent.children.name];
         [self.view addSubview:label];
    
         UITextField *textfiled = [[UITextField alloc]initWithFrame:CGRectMake(100, 300, kScreenWidth-200, 40)];
         textfiled.placeholder = @"重新输入孩子的名字";
         [textfiled addTarget:self action:@selector(textfiledChanged:) forControlEvents:UIControlEventEditingChanged];
         [self.view addSubview:textfiled];
    
     }
    
     - (void)textfiledChanged:(UITextField *)textfiled{
    
         parent.children.name = textfiled.text;
     }
    
     /*
      * 如果监听的属性发生了变化,调用该方法。
    
      * keyPath: 传进来发生变化的属性。
    
      * object: 所监听的对象
    
      * change:是一个字典,包含了与property的值变化相关的信息。其中可能会有这样几个键值对,
    
         NSKeyValueChangeKindKey:这是change中永远会包含的键值对,它的值时一个NSNumber对象,具体的数值有NSKeyValueChangeSetting(对属性进行赋值操作)、NSKeyValueChangeInsertion(对属性进行插入操作)、NSKeyValueChangeRemoval(对属性进行移除操作)、NSKeyValueChangeReplacement(对属性进行替换操作)这几个,其中后三个是针对于一对多关系的。
    
         NSKeyValueChangeNewKey:只有当addObserver的时候在optional参数中加入NSKeyValueObservingOptionNew,这个键值对才会被change参数包含;它表示这个property改变后的新值。
    
         NSKeyValueChangeNewOld:只有当addObserver的时候在optional参数中加入NSKeyValueObservingOptionOld,这个键值对才会被change参数包含;它表示这个property改变前的值。
    
         NSKeyValueChangeIndexesKey:当被观察的property是一个ordered to-many relationship时,这个键值对才会被change参数包含;它的值是一个NSIndexSet对象。
    
         NSKeyValueChangeNotificationIsPriorKey:只有当addObserver的时候在optional参数中加入NSKeyValueObservingOptionPrior,这个键值对才会被change参数包含;它的值是@YES。
    
         用[change objectForKey:@"old"]获取变化前的值
         用[change objectForKey:@"new"]获取变化后的值
    
      *
      */
     - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    
         label.text = [NSString stringWithFormat:@"%@的孩子叫做%@",parent.name, [change objectForKey:@"new"]];
    
         NSLog(@"上次的名字是%@",[change objectForKey:@"old"]);
    
     }
    
     - (void)dealloc{
    
         [parent removeObserver:self forKeyPath:@"children.name"];
    
     }
    
     @end

1、 添加观察者时的options参数,目前我们最为常用的就是前三种,其他两种可不做深究。
2、属性发生变化,调用相应的方法,change字典参数最常用的两个键是『new』和『old』,其他暂不考虑。

附录 Parent.h和Children.h

Parent.h,.m里什么都没有
#import <Foundation/Foundation.h>
#import "Children.h"

@interface Parent : NSObject

@property (nonatomic,copy) NSString *name;

@property (nonatomic,retain) Children *children;

@end
Children.h .m里什么都没有
#import <Foundation/Foundation.h>

@interface Children : NSObject

@property (nonatomic,copy) NSString *name;

@end


1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.m或d论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 、1资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值