day3---通知、KVC/KVO、XML解析

一 通知
1.1 概念
通知(Notification)是观察者模式的一种具体表现,可以理解为广播模式,广播模式也是一种设计模式,类似于生活中的广播,比如:收音机。。

1.2 作用
一个对象不用知道消息的接受者是谁,就可以将一些消息发送给需要的接受者,要求,这些接受者必须先注册这个通知;
发送消息的对象无法知道有哪些对象,有多少个对象接受消息,也不知道这些对象是否存在,   
1.3 如何使用?
1)收听者打开收音机听广播,即找到一个通知中心NSNotificationCenter;
2)设置收听的频道,即注册收听哪个具体的频道(addObserver专门用来注册的方法)
3)发送者首先找到通知中心NSNotificationCenter,创建具体的通知对象NSNotification,使用通知中心发送通知;
4)收听者收到消息,处理消息响应相关的方法;
5)在不需要收听消息的时候(当对象销毁的时候),停止收听,找到通知中心,注销之前注册的那个通知(removeObserver)


1.4 NSNotificationCenter
NSNotificationCenter通知中心是一个单例对象,以保证通知中心只有一个NSNotification通知对象的类型;
NSNotification?
是一个通知对象的类型,用于封装通知的内容,参数等,通过他,把消息传递给观察者;

【注】比如一个对象A要发送通知,其中有对象B,C,D都来接受这个通知,需要事项以下步骤:
在     A   中:找到通知中心,创建通知对象,存储通知的内容,发送通知;
在B,C,D中:找到通知中心,并注册收听的某个频道;当A发消息时,B,C,D就会对应的响应相关的方法,处理功能。。

【注:一定要确保“当A开始发消息的时候,B,C,D这三个对象已经注册了”】

见【Demo】-【1-NSNotifacationCenter】

发送者找到通知中心,并发送信息,同时给消息命名:

  • (IBAction)changeColorBtnClick:(id)sender {

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:@"选择颜色" preferredStyle:UIAlertControllerStyleActionSheet];

    NSArray *arrayTitle = @[@"红色",@"蓝色",@"紫色",@"黄色"];
    NSArray *arrayColor = @[[UIColor redColor],[UIColor blueColor],[UIColor purpleColor],[UIColor yellowColor]];

for (int i=0; i<4; i++) {
    
    UIAlertAction *action = [UIAlertAction actionWithTitle:arrayTitle[i] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
        //发送通知
        //第一个参数:给该条通知命名;
        [[NSNotificationCenter defaultCenter] postNotificationName:@"changedColor" object:arrayColor[i]];
        self.view.backgroundColor = arrayColor[i];
        
    }];
    
    [alert addAction:action];
}

[self presentViewController:alert animated:YES completion:nil];

}

接受者找到通知中心,根据消息名称注册通知;最后销毁注册的通知
//当我们使用storyboard创建对象时,默认初始化方法调用该方法

  • (instancetype)initWithCoder:(NSCoder *)coder
    {
    self = [super initWithCoder:coder];
    if (self) {

      //在初始化方法中注册通知
      //在通知中心,注册self为“changedColor”这条广播的观察者,一旦有任何其他对象,通知通知中心发送changedColor这条广播,self就能接受到,进而触发方法
      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notification:) name:@"changedColor" object:nil];
    

    }
    return self;
    }

-(void)notification:(NSNotification *)noti
{
//noti.object 该消息对象传的参数
self.view.backgroundColor = noti.object;

}

  • (void)dealloc
    {
    //当对象销毁时,注销通知中心所注册的通知
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"changedColor" object:nil];

}

二 KVC/KVO
2.1 KVC 【** 重点 **】
KVC的全称“key-Value-Coding”键值编码
特点:
1)通过setValue:forKey:形式为帝乡的属性赋值,可以为对象的成员变量赋值;其中Key表示属性的名称,Value表示属性的值;

2)通过路径存取值  setValue: forKeyPath:

3)把字典转换成数据模型【开发中常用,重点】

见【Demo】-【2-KVC】

1)****************************************************************
//setValue: forKey: 的形式赋值
// 用kvc的形式给成员变量,属性赋值
[per1 setValue:@"男" forKey:@"_sex"];
[per1 setValue:@"老谈" forKey:@"name"];
NSLog(@"sex=%@",[per1 valueForKey:@"_sex"]);
NSLog(@"name=%@",per1.name);

2)****************************************************************
//通过路径存取值 setValue: forKeyPath:
//1.要保证路径上的属性对象是存在的
//2.路径上的key要与属性的名字一致
[per1 setValue:@100 forKeyPath:@"book.price"];
[per1 setValue:@"水浒传" forKeyPath:@"book.name"];

NSLog(@"price:%@",[per1 valueForKeyPath:@"book.price"]);
NSLog(@"name:%@",[per1 valueForKeyPath:@"book.name"]);

3)****************************************************************

Person *per2 = [[Person alloc] init];
NSDictionary *dict = @{@"age":@22,@"name":@"张三",@"sex":@"男",@"numberID":@"1001"};
[per2 setValuesForKeysWithDictionary:dict];                 【** 重点 **】

NSLog(@"age:%d",per2.age);
NSLog(@"name:%@",per2.name);
NSLog(@"sex:%@",[per2 valueForKey:@"sex"]);

//防止给没有声明的成员变量或者属性赋值,写下面这个方法:
//重写这个方法,当key在当前类中没有找到对应的属性名称时,会执行这个方法,重写之后,防止崩溃;
-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{

}

//Person中写一个方法mySetValuesForKeysWithDictionary; 【简化,只有属性】
-(void)mySetValue:(id)value forKey:(NSString *)key
{
//value 20 key age
NSString *newStr = [NSString stringWithFormat:@"set%@:",[key capitalizedString]];

//根据字符串反射出对应的方法名
SEL sel = NSSelectorFromString(newStr);

[self performSelector:sel withObject:value];
�

}

//Person中写一个方法mySetValue: forKey:
-(void)mySetValuesForKeysWithDictionary:(NSDictionary *)dict
{
for (NSString *key in dict) {

    [self setValue:dict[key] forKey:key];
}

}

2.2 KVO
全称Key-Value-Observer,称为“观察者模式”;对符合KVC键值编码的变量的观察,主要是对变量的赋值操作的观察,如果一旦被观察的变量发生了改变,那么则,通知观察者,执行相关方法;

比如:
有一个数据模型Model,其中有一个属是NSString *message,我们在VC上,用一个UILabel显示这个值;只要message发生改变,那么就要对应的更新UILabel的显示;
界面A和界面B,在界面A有一个UILabel,显示这个model的message的值;
由界面Apush到界面B,在界面B中有一个UITextField,可以修改这个model对象的message的值,每次修改之后,对应的界面A上面的label就能对应的更新显示;

目前能实现这样的一个功能:
1.代理    2.单例    3.block     4.通知    5KVC/KVO
使用KVC/KVO设计模式,先设置A作为观察者,观察model的message的值是否发生改变;如果发生改变,自动的调用某个方法;

见【Demo】-【3-KVO】
  • (void)viewDidLoad {
    [super viewDidLoad];

    self.model = [[MyModel alloc] init];

    //kvo观察model的属性,那么必须使用kvc给model的属性赋值
    [self.model setValue:@"我是旧值🐑" forKey:@"message"];
    //把当前对象注册为self.model的属性message的观察者
    //第一个参数:谁作为观察者
    //第二个参数:观察的是哪个成员变量
    //第三个参数:枚举值,表示观察的新值还是旧值
    [self.model addObserver:self forKeyPath:@"message" options:NSKeyValueObservingOptionNew context:nil];
    self.myLabel.text = self.model.message;
    }

//那么一旦,该model的成员变量message发生改变,就会自动让观察者调用下面的这个方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"message"]) {
self.myLabel.text = change[@"new"];
}

}

//故事版中界面跳转传值在下面方法中进行:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
SecondViewController *secVc = segue.destinationViewController; //取得即将跳转的界面
secVc.model = self.model;

}

//在其他界面修改值
-(void)viewWillDisappear:(BOOL)animated
{
//使用KVC来修改
[self.model setValue:self.myField.text forKey:@"message"];

}

小知识:
运用GData进行解析XML文件的时候,做以下三件事:
1.Build Settings —>搜索header search 将header search Paths 路径改为/usr/include/libxml2
2.Build Phases —>在第二个目录中相应文件添加 -fno-objc-arc ,让ARC和MRC兼容
3.Build Phases —>在第三个目录添加新问价,搜索XML,添加libxml2.dylib

三 XML

+(NSArray *)arrayWithXMLData:(NSData *)data;

+(NSArray *)arrayWithXML2Data:(NSData *)data;

import "GDataXMLNode.h" //需要导入这个头文件

+(NSArray *)arrayWithXMLData:(NSData *)data
{
NSMutableArray *mArr = [NSMutableArray array];
/********** 第一种方式 *************/
//1.创建xml解析器
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:data options:0 error:nil];
//判断xml解析器是否创建成功
if (doc)
{
//2.取得该xml数据的根节点(所有的节点都是GDataXMLElement类型的对象)
GDataXMLElement *rootElment = [doc rootElement];

    //3.通过根节点拿到子节点(books),返回的是一个数组,数组里面存放的是所有的books节点
    NSArray *booksArr = [rootElment elementsForName:@"books"];
    
    GDataXMLElement *booksElement = [booksArr lastObject];
    
    
    //4.取出books节点下的所有的book节点
    NSArray *bookArr = [booksElement elementsForName:@"book"];
    
    //5.遍历数组的bookArr
    for (GDataXMLElement *element in bookArr) {
        BookModel *model = [[BookModel alloc] init];
        model.name = [[[element elementsForName:@"name"] lastObject] stringValue];
        model.price = [[[element elementsForName:@"price"] lastObject] stringValue];
        model.summary = [[[element elementsForName:@"summary"] lastObject] stringValue];
        
        GDataXMLElement *autherElement = [[element elementsForName:@"auther"] lastObject];
        model.auther = [[[autherElement elementsForName:@"name"] lastObject] stringValue];
        
        [mArr addObject:model];
    }
}
return mArr;

}

+(NSArray *)arrayWithXML2Data:(NSData *)data
{
NSMutableArray *mArr = [NSMutableArray array];
/******** 第二种方法 *********/
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:data options:0 error:nil];
if (doc) {
//根据xpath语句提供的节点的路径,渠道对应的节点,放到数组中
//@“/root/books/book”
//@“//book” 拿到所有的book节点
NSArray *array = [doc nodesForXPath:@"//book" error:nil];
for (GDataXMLElement *element in array) {
BookModel *model = [[BookModel alloc] init];
model.name = [[[element elementsForName:@"name"] lastObject] stringValue];
model.price = [[[element elementsForName:@"price"] lastObject] stringValue];
model.summary = [[[element elementsForName:@"summary"] lastObject] stringValue];

        GDataXMLElement *autherElement = [[element elementsForName:@"auther"] lastObject];
        model.auther = [[[autherElement elementsForName:@"name"] lastObject] stringValue];
        [mArr addObject:model];
        
        
        //取得节点的属性(GDataXMLNode)
        GDataXMLNode *idNode = [element attributeForName:@"id"];
        GDataXMLNode *lanNode = [element attributeForName:@"language"];
        NSLog(@"id = %@,lan = %@",[idNode stringValue],[lanNode stringValue]);
        
    }

}
return mArr;

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容