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


        我们在第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



  • 相关阅读:
    阿里应该不应该给公众一个交代——教师使用钉钉上网课“遭黑客攻击网暴”后死亡
    tensorflow1.x——如何在C++多线程中调用同一个session会话
    偶然间发现一个还不错的仪表盘界面——感觉很不错的界面设计
    国产深度学习框架MindSpore的高校发展之旅——“样本点”计划
    记录一次实验室linux系统的GPU服务器死机故障的排查——Linux系统的Power States
    tensorflow1.x——如何在python多线程中调用同一个session会话
    对欠曝光图片的处理
    如何使用git通过ssh协议拉取gitee上的项目代码——如何正确的免密使用git
    再用国产操作系统deepin出现拖影现象
    记录一次实验室linux系统的GPU服务器死机排查过程——某显卡满负荷导致内核进程超时导致系统死机
  • 原文地址:https://www.cnblogs.com/mthoutai/p/6847232.html
Copyright © 2020-2023  润新知