《苹果开发之Cocoa编程》键-值编码和键-值观察

时间:2024-01-12 12:05:08

一、KVC

键-值编码(Key - Value Coding, KVC)是通过变量名的读取和设置变量值的一种方法,将字符串的变量名作为key来引用。NSObject定义了两个方法(KVC方法)用于变量值得读取和设置:setValue:forKey; valueForKey:forKey;

@interface Student:NSObject{
    NSString *firstName;
}

Student *s = [[Student alloc] init];

//设置变量值
[s setValue:@"Larry" forKey:@"firstName"];
//读取变量值
NSString *name = [s valueForKey:@"firstName"];

注:KVC两个方法操作的都是对象,如变量类型是int等标量,必须转换为对象。例如int类型转换为NSNumber类型 [NSNumber numberWithInt: 5]

二、accessor

accessor 类似于C#里的使用属性读取和设置变量值。objective-c里accessor读取和设置方法的写法是有规定的,如@interface定义了NSString *name变量,则accessor的方法要定义为-(NSString *)name; 和 -(void)setName:(NSString *)value; 即读取方法的名称和变量名相同,返回类型为变量类型;写方法名称为“set变量名”(变量名第一个字母大写)带一个和变量相同类型的参数。

//头文件
@interface AccessorDemo:NSObject{
    NSString *name;
}

-(NSString *)name;
-(void)setName:(NSString *)value;

@end;

//实现文件
#impot AccessorDemo.h

@implementation AccessorDemo

-(NSString *)name{
    return name;
}

-(void)setName:(NSString *)value{
    name = value;
}

@end;

三、KVC和accessor的关系

如果定义了accessor方法,则KVC的setValue方法调用accessor的写方法,KVC的valueForKey方法调用accessor的读方法。

例如下面的代码:

//
//  KvcFunAppDelegate.h
//  KvcFun
//
//  Created by apple on 13-7-12.
//  Copyright (c) 2013年 FDStudio. All rights reserved.
//

#import <Cocoa/Cocoa.h>

@interface KvcFunAppDelegate : NSObject <NSApplicationDelegate>{
    int fido;
}

-(int)fido;
-(void)setFido:(int)x;

@property (assign) IBOutlet NSWindow *window;

@end
//
//  KvcFunAppDelegate.m
//  KvcFun
//  Created by apple on 13-7-12.
//  Copyright (c) 2013年 FDStudio. All rights reserved.
//

#import "KvcFunAppDelegate.h"

@implementation KvcFunAppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
}

-(id)init{
    self = [super init];
    if(self){
        [self setValue:[NSNumber numberWithInt:] forKey:@"fido"]; //KVC(Key-Value Coding)只能处理对象,所以要将int转换为NSNumber
        NSNumber *n = [self valueForKey: @"fido"];
        NSLog(@"fido = %@", n);
    }
    return self;
}

-(int)fido{
    NSLog(@"-fido is return %d", fido);
    return fido;
}

-(void)setFido:(int)x{
    NSLog(@"-setFido is called with %d", x);
    fido = x;
}
@end

《苹果开发之Cocoa编程》键-值编码和键-值观察

四、点标记方法

类型于C#里“对象.属性"的写法,以简化accessor的调用。正常的objective-c的accessor调用方法类似于value = [object name] [object setName: @"李四"]的写法。而点标记方法的写法为 value = object.name 和 object.name = @"李四"。但某些人不建议用,为什么应该是仁者见仁智者见智了。

五、Properties

properties提供了一个方法来减少写accessor的代码量。property的声明语法为 @property (attritbutes) type name; 其中type为变量类型 name为变量名称。实现文件中的写法为@synthesize name;如果property名字和成员变量名称不一样,如xcode的习惯写法为成员变量加下划线前缀,则用@synthesize name = 成员变量名称。

attributes属性可包括readwrite 和 readonly两种,默认是readwrite。设置为readonly则没有setter方法。为描述setter方法如何工作,属性包括下面的一种:assign、strong、weak、copy。

  • assign(默认) 创建一个赋值语句,常用于标量变量,而非指针类型的变量。
  • strong 说明property是强引用类型
  • weak 说明property是弱引用类型
  • copy 创建新值得复制,常用于字符串和其他mutable子类型情况下。

strong 和 weak 区别不好理解,最好看看objective-c的retain引用计数部分作为基础。看了http://mobile.51cto.com/iphone-386301.htm的文章有点明白了。

定义为strong变量,如果有其它变量引用了strong变量则其引用计数+1,weak变量引用计数则不+1;仔细看下面的代码和输出结果就明白了。

1.强引用

//头文件内容
@property (strong) NSString *string1;
@property (strong) NSString *string2;

//实现文件内容
@synthesize string1;
@synthesize string2;

下面代码测试输出结果

self.string1 =[[NSStringalloc] initWithUTF8String:"string1"];
self.string2 = self.string1;
self.string1 = nil;

NSLog(@"String 2 = %@", self.string2);

输出结果为

《苹果开发之Cocoa编程》键-值编码和键-值观察

2.弱引用

如果将string1 或者 string2任何一个指定为weak,则输出下面的结果

《苹果开发之Cocoa编程》键-值编码和键-值观察

即声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil。这样的好处能有效的防止野指针。

注:string1的复制必须用[[NSStringalloc] initWithUTF8String:"string1"],否则weak的运行结果和strong相同。如string1 = @“string1”这样赋值就不行,weak和strong的结果一样,具体为什么没搞明白呢,求高手指点!

 六、绑定和键-值观察KVO

Cocoa中许多控件对象都使用绑定,当开发者绑定一个键到控件的一个属性时,显示视图将自动让它们同步。例如一个滑动条的Value和一个整形键值fido绑定。滑动条使用valueForKey来获取初值(触发了accessor的fido方法),当移动滑动条时调用setValue来更新fido的值(触发了accessor的setFido方法)。

如果直接改变成员变量的值,就不会通知观察者(滑动条)键值已经改变,视图也就不能同步了。需要显式的通知观察者,改变成员变量前调用willChangeValueForKey方法,改变变量后调用didChangeValueForKey方法。例如:

[self willChangeValueForKey:@"fido"];
fido++;
[self didChangeValueForKey:@"fido"];

另两张手动改变变量值,同步观察者的方法为使用KVC方法或accessor方法。

NSNumber *n = [self valueForKey:@"fido"];
NSNumber *newValue = [NSNumber numberWithInt: [n intValue] + ];
[self setValue:newValue forKey:@"fido"];
[self setFido:[self fido] + ];

实例练习:

《苹果开发之Cocoa编程》键-值编码和键-值观察

1.新建一个窗体应用程序,名称和类前缀都为KvcFun。

2.窗体上添加一个水平滑动条、一个lable、3个按钮。

3.KvcFunAppDelegate.h中添加int _fido变量,用于记录滑动条的值,然后建立@property (assing) fido;属性。KvcFunAppDelegate.m添加property的实现

@synthesize fido = _fido;。

4.绑定控件的属性,将滑动条和Label的value属性绑定到fido键。

《苹果开发之Cocoa编程》键-值编码和键-值观察

《苹果开发之Cocoa编程》键-值编码和键-值观察

5.设置滑动条的初始值

-(id)init{
    self = [super init];
    if(self){
        [self setFido: ];
    }
    return self;
}

6.添加三个按钮的IBAction方法

(1)手动方法将滑动条的值加1

- (IBAction)handIncementFido:(id)sender {
    [self willChangeValueForKey:@"fido"];
    _fido++;
    [self didChangeValueForKey:@"fido"];
}

(2)KVC方法将滑动条的值加1

- (IBAction)kvcIncementFido:(id)sender {
    NSNumber *n = [self valueForKey: @"fido"];
    NSNumber *newVal = [NSNumber numberWithInt:[n intValue] + ];
    [self setValue:newVal forKey:@"fido"];
}

(1)accessor方法将滑动条的值加1

- (IBAction)incrementFido:(id)sender {
    [self setFido: [self fido] +  ];
}

7.运行测试效果,点我下载源代码《苹果开发之Cocoa编程》键-值编码和键-值观察

七、键路径

未理解,to-do this :(