我们在第16和第17篇中分别介绍了obj-c的KVC与KVO特性,当时举的例子比较fun,太抽象,貌似和实际不沾边哦。那么下面我们就用一个实际中的例子来看看KVC与KVO是如何运用的吧。

该例中用到了3种新的控件类型:NSTableView、NSSlider以及简单的NSTextField类型。按说不能再在Random类里添加不沾边的新增功能了,但是为了简单,我还是把所有东西都放在Random类里喽。程序运行时界面如下:

大家可以看到左上角的文本域控件用来显示当前音量,因为它和Random类里的str_volume(或者是str_vol_way2以及str_vol_way3)属性做了绑定,所以它会即时更新音量变化的数值;而文本域控件下方的刻度条控件可以让用户更改音量大小(从0 到 100),它和Random类的属性volume绑定起来,所以改变刻度就会带来volume属性值的变化,相当于对volume属性做写者操作哦。有童鞋可能不明白文本域是如何随着刻度条值的变化而变化的,毕竟他们绑定的不是一个属性啊!

其实我在这里用了KVO的概念,不过这里我用自己监视的是自己的属性啊:在Random的init方法中设置被观察者为self,而观察者也是self,观察的KeyPath为@"volume",于是乎若刻度条将volume改变(因为它和volume绑定,所以它刻度的改变会导致volume的改变,上面已经说了,会调用volume的写者方法。),则self,也就是Random的对象自己会收到通知。不过这里要注意,不能够直接在通知回调方法中直接写:str_volume
= [新值] ,因为你这样没有调用str_volume的写者方法,你是直接改实例变量本身了,这样文本域不会有变化的:因为文本域控件也对str_volume做了KVO,而且该KVO只能监视到str_volume被写者方法改变的情况,你直接改它实例变量,自然没有反应喽。要解决这个问题,可以有3种方法:

1 将str_volume定义在interface,并声明属性,然后在implementation里做@synthesize str_volume,这样我们用[self setStr_volume或self.str_volume的方法(实例变量str_volume的写者方法)修改,自然会通知外部监控器。

2 手动发送通知,告知属性被改了,这也是str_vol_way2的方法;

3 用属性的KVC方式修改其值,这也是str_vol_way3的方法啊(注意:str_vol_way3没有外部接口哦!)。

然后在人语发声之前用speech的setVolume方法应用当前的音量大小就可以啦,不过注意该方法setVolume的参数是浮点数,取值范围为0.0 - 1.0,所以我在代码中要除以100啊!

再看窗口右上方的列表视图控件,也有几个地方要注意:

1 它是由几个控件组成的,鼠标要多选几次才能选中里面的控件,如果控件选的不对,可能就找不到要设置属性喽:

2 其Connections Inspector中要连接2个地方:dataSource和delegate。前者用来做数据源的代理,后者做其本身动作的代理哦。如果不设置前者则没有数据源,就没东西显示啊;同理,若不设置后者就无法响应用户的动作哦。切记切记。这里再说说代理,代理就是你调用别的类,可是有些事还是你自己最清楚,所以别的类的有些操作还是得返回来问你自己啊。比如列表视图控件对于[col,row]位置显示的内容是不知道的,所以你必须以回调方法的方式告诉它;再者,如果它的当前选中行发生变化了,他也不知道如何处理,所以也要问你,等于是一个当前行改变的事件发生了,Random类必须提供事件处理函数哦。

最后,NSTableView控件还是要按老规矩和Random类连接起来啊,就在其Referencing Outlets里哦;也就是说它和Random一共发生了3种显式关系(想歪的自觉面壁去)如下图所示:

好了,上面把主要的问题都大致说过了,啥也不说鸟,下面的都在代码里喽(在Cocoa实例02的代码基础上修改而来):

//
//  Random.h
//  mac_test
//
//  Created by kinds on 14-7-4.
//  Copyright (c) 2014年 kinds. All rights reserved.
//

#import "comm.h"
#import <Cocoa/Cocoa.h>

@interface Random : NSObject {
	IBOutlet NSTextField *text_field;
	IBOutlet NSTableView *tab_view;
	NSString *str_volume;
}

@property NSString *str_volume;

-(IBAction)seed:(id)sender;
-(IBAction)generate:(id)sender;

@end
//
//  Random.m
//  mac_test
//
//  Created by kinds on 14-7-4.
//  Copyright (c) 2014年 kinds. All rights reserved.
//

#import "Random.h"

@implementation Random{
	NSSpeechSynthesizer *speech;
	NSArray *voices;
	NSNumber *volume;
	NSString *str_vol_way2;
	NSString *str_vol_way3;
}

@synthesize str_volume;

-(id)init{
	self = [super init];
	if(self){
		speech = [[NSSpeechSynthesizer alloc] initWithVoice:nil];
		voices = [NSSpeechSynthesizer availableVoices];
		msg(@"%@",voices);

		volume = [NSNumber numberWithInt:0];
		str_volume = [NSString stringWithFormat:@"音量:%@",volume];
		str_vol_way2 = str_vol_way3 = str_volume;
		[self addObserver:self forKeyPath:@"volume" \
				  options:NSKeyValueObservingOptionNew context:nil];
	}
	return self;
}

-(void)observeValueForKeyPath:(NSString *)key_path ofObject:(id)obj \
	change:(NSDictionary *)change context:(void *)context{
		NSNumber *new_val = [change objectForKey:NSKeyValueChangeNewKey];
		msg(@"volume is change to %@",new_val);
		//str_volume = @"A";
		self.str_volume = [NSString stringWithFormat:@"音量:%i",[new_val intValue]];

		//syn way 2
		[self willChangeValueForKey:@"str_vol_way2"];
		str_vol_way2 = str_volume;
		[self didChangeValueForKey:@"str_vol_way2"];
		//syn way 3
		[self setValue:str_volume forKey:@"str_vol_way3"];
}

-(void)set_voice{
	int idx = (int)(random() % [voices count]);
	[speech setVoice:[voices objectAtIndex:(NSUInteger)idx]];
}

-(IBAction)generate:(id)sender{
	int i = (int)(random() % 100000000000) + 1;
	msg(@"i = %d",i);
	[text_field setIntValue:i];
	[self set_voice];
	//[speech setVolume:[volume floatValue]];
	[speech startSpeakingString:[NSString stringWithFormat:@"%i",i]];
}

-(IBAction)seed:(id)sender{
	srandom((unsigned)time(NULL));
	NSString *str = @"the seed is reseted!";
	[text_field setStringValue:str];
	[speech startSpeakingString:str];
}

-(void)awakeFromNib{
	NSDate *now = [NSDate date];
	[text_field setObjectValue:now];
}

-(NSInteger)numberOfRowsInTableView:(NSTableView *)tv{
	return (NSInteger)[voices count];
}

-(id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)col \
row:(NSInteger)row{
	NSString *v = [voices objectAtIndex:row];
	return v;
}

-(void)tableViewSelectionDidChange:(NSNotification *)notification{
	NSInteger row = [tab_view selectedRow];
	if(row == -1) return;

	NSString *str_voice = [voices objectAtIndex:row];
	[speech setVoice:str_voice];
	[speech setVolume:[volume floatValue]/100];
	[speech startSpeakingString:@"test one time!!!测试一下哦!!!"];
	msg(@"new voice = %@",str_voice);
}

-(void)dealloc{
	[self removeObserver:self forKeyPath:@"volume"];
}

@end

obj-c编程15[Cocoa实例02]:KVC和KVO的实际运用的更多相关文章

  1. [原创]obj-c编程15[Cocoa实例02]:KVC和KVO的实际运用

    原文链接:obj-c编程15[Cocoa实例02]:KVC和KVO的实际运用 我们在第16和第17篇中分别介绍了obj-c的KVC与KVO特性,当时举的例子比较fun,太抽象,貌似和实际不沾边哦.那么 ...

  2. obj-c编程15[Cocoa实例01]:一个会发声的随机数生成器

    哇!终于到了obj-c编程系列的第15篇喽,一路走过来满不容易的哦!(怎么个意思,这才哪到哪啊!),为了能够更好的练习obj-c在Cocoa框架上的编程,接下来会以N篇Cocoa实例的博文来巩固和记忆 ...

  3. obj-c编程15[Cocoa实例03]:MVC以及归档化演示样例

    前面的博文里介绍了归档和解档,这里我们把它实际应用到一个简单的代码中去,将它作为一个多文档应用程序的打开和保存的背后支持.另外这里介绍一下MVC思想,这个在不论什么语言里都会有,它是一种设计思想,主要 ...

  4. obj-c编程15[Cocoa实例04]:基于Core Data的多文档程序示例[未完待续]

    上一个例子我们使用的模式数据实际上是基于一个Person数组,现在我们看一下如何使用Cocoa中的Core Data框架支持,几乎不用写一行代码,完成模式数据的建立. 我们这里模式的元素使用的是Car ...

  5. obj-c编程15[Cocoa实例03]:MVC以及归档化示例

    前面的博文里介绍了归档和解档,这里我们把它实际应用到一个简单的代码中去,将它作为一个多文档应用程序的打开和保存的背后支持.另外这里介绍一下MVC思想,这个在任何语言里都会有,它是一种设计思想,主要可以 ...

  6. 谈KVC、KVO(重点观察者模式)机制编程

    一不小心,小明在<跟着贝尔去冒险>这个真人秀节目中看到了“一日警察,一世警察”的Laughing哥,整个节目除了贝尔吃牛睾丸都不用刀叉的不雅餐饮文化外,还是镜头少普通话跟小明一样烂的Lau ...

  7. [原创]obj-c编程16:键值编码(KVC)

    原文链接:obj-c编程16:键值编码(KVC) 我们可以借助obj-c中的键值编码(以后简称KVC,Key-Value Coding)来存取类的属性,通过指定所要访问的属性名字符串标示符,可以使用存 ...

  8. C#网络编程TCP通信实例程序简单设计

    C#网络编程TCP通信实例程序简单设计 采用自带 TcpClient和TcpListener设计一个Tcp通信的例子 只实现了TCP通信 通信程序截图: 压力测试服务端截图: 俩个客户端链接服务端测试 ...

  9. 零元学Expression Blend 4 - Chapter 15 用实例了解互动控制项「Button」I

    原文:零元学Expression Blend 4 - Chapter 15 用实例了解互动控制项「Button」I 本章将教大家如何更改Button的预设Template,以及如何在Button内设置 ...

随机推荐

  1. 学习TensorFlow,保存学习到的网络结构参数并调用

    在深度学习中,不管使用那种学习框架,我们会遇到一个很重要的问题,那就是在训练完之后,如何存储学习到的深度网络的参数?在测试时,如何调用这些网络参数?针对这两个问题,本篇博文主要探索TensorFlow ...

  2. 【java虚拟机系列】JVM类加载器与ClassNotFoundException和NoClassDefFoundError

    在我们日常的项目开发中,会经常碰到ClassNotFoundException和NoClassDefFoundError这两种异常,对于经验足够的工程师而言,可能很轻松的就可以解决,但是却不一定明白为 ...

  3. <<精通iOS开发>>第14章例子代码彻底清除警告

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 上一篇我们解决了<<精通iOS开发>> ...

  4. (九十七)集成JPush实现远程通知和推送的发送

    上节介绍了通过直接和APNS交互实现推送的方法,较为繁琐,最为重要的是发送推送需要特定的服务端,通过JPush,不仅可以简化客户端的接收,还可以通过控制台或者API实现通知的发送. 首先注册JPush ...

  5. 最简单的基于FFmpeg的封装格式处理:视音频分离器(demuxer)

    ===================================================== 最简单的基于FFmpeg的封装格式处理系列文章列表: 最简单的基于FFmpeg的封装格式处理 ...

  6. UNIX环境高级编程——线程私有数据

    线程私有数据(Thread-specific data,TSD):存储和查询与某个线程相关数据的一种机制. 在进程内的所有线程都共享相同的地址空间,即意味着任何声明为静态或外部变量,或在进程堆声明的变 ...

  7. Jeff Atwood:Google的头号UI问题

    谷歌在用户界面上追求的"极简主义"是让人叹为观止的.但是,他们首页上有个问题一直让我困惑不解.要知道,这个页面可是每天都被下载几百万次哦: 真有人在使用"I'm Feel ...

  8. 敏捷测试(3)--基于story的敏捷基础知识

    基于story的敏捷基础知识----story编写 为什么使用Story? 软件行业40年多来,需求分析技术已经很成熟了,但是MRD驱动的过程不堪重负.因为往往MRD编写会占去很多时间,MRD评审又会 ...

  9. [WinForm]最小化到系统托盘,右键退出

    1.拉出一个notifyIcon1到用户界面,也可以NEW一个 2.拉出一个ContextMenuStrip控件,命名为mymenu,集合中增加退出 3.notifyIcon1的属性ContextMe ...

  10. Dynamics CRM2013 Lookup Filtering using addCustomFilter

    dynamics crm中对lookup视图的过滤是一个很平常性的需求,在2011的时候都是用添加自定义视图的方式例如下面这段示例代码 <span style="font-size: ...