许多编程技术都基于间接机制,包括整个面向对象编程领域。本章将介绍另一种间接机制,这种机制不属于Obj-C语言的特性,而是Cocoa提供的一种特性。
我们知道在C#中可以通过反射读写一个对象的属性,有时候这种方式特别方便,因为你可以利用字符串的方式去动态控制一个对象。其实由于ObjC的语言特性,你根本不必进行任何操作就可以进行属性的动态读写,这种方式就是Key Value Coding(简称KVC)。
一些更加高级的Cocoa特性,例如Core Data和Cocoa Bindings(本书将不介绍该内容),在基础机制中包含了KVC。
KVC的操作方法由NSKeyValueCoding协议提供,而NSObject就实现了这个协议,也就是说ObjC中几乎所有的对象都支持KVC操作。
Object-c
常用的KVC操作方法
动态设置
- (void)setValue:(nullable id)value forKey:(NSString *)key;
(用于简单路径)- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
(用于复合路径,例如Person有一个Account类型的属性,那么person.account就是一个复合属性)- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues
;(用于字典传入,大数据赋值)
动态读取
- (nullable id)valueForKey:(NSString *)key;
(简单路径取值)- (nullable id)valueForKeyPath:(NSString *)keyPath;
(复合路径取值)- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
(批量取值,可以一致性提取多个数据)
例子
模型数据
Address.h
//
// Address.h
// KVC
//
// Created by yangjun on 15/10/10.
// Copyright © 2015年 六月. All rights reserved.
//
#import <Foundation/Foundation.h>
/** 地址*/
@interface Address : NSObject
@property (nonatomic, copy) NSString *addressName;///< 地址名
@end
User.h
//
// User.h
// KVC
//
// Created by yangjun on 15/10/10.
// Copyright © 2015年 六月. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Address.h"
/** 用户*/
@interface User : NSObject
@property (nonatomic, copy) NSString *userName; ///< 用户名
@property (nonatomic, strong) Address *address; ///< 用户地址
@end
XCTestCase测试
//
// KVCTests.m
// KVCTests
//
// Created by yangjun on 15/10/10.
// Copyright © 2015年 六月. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "User.h"
@interface KVCTests : XCTestCase
{
User *_user;
}
@end
@implementation KVCTests
- (void)setUp {
[super setUp];
_user = [[User alloc] init];
}
- (void)testExample {
// 简单路径
[_user setValue:@"yangjun" forKey:@"userName"];
NSString *value = [_user valueForKey:@"userName"];
Address *address = [[Address alloc] init];
address.addressName = @"BeiJing";
[_user setValue:address forKey:@"address"];
value = [_user valueForKey:@"userName"];
NSLog(@"用户名:%@;地址:%@", _user.userName, _user.address.addressName);
// 复合路径
[_user setValue:@"君" forKeyPath:@"userName"];
[_user setValue:@"北京" forKeyPath:@"address.addressName"];
value = [_user valueForKeyPath:@"address.addressName"];
NSLog(@"用户名:%@;地址:%@", _user.userName, _user.address.addressName);
// 字典传入
address = [[Address alloc] init];
address.addressName = @"北京站";
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"阳", @"userName", address, @"address", nil];
[_user setValuesForKeysWithDictionary:dict];// 设值
dict = [_user dictionaryWithValuesForKeys:[NSArray arrayWithObjects:@"userName", @"address", nil]];// 取值
NSLog(@"用户名:%@;地址:%@", _user.userName, _user.address.addressName);
NSLog(@"%@", dict);
}
@end
输出
2015-10-10 13:51:17.453 KVC[8450:699448] 用户名:yangjun;地址:BeiJing
2015-10-10 13:51:59.677 KVC[8450:699448] 用户名:君;地址:北京
2015-10-10 13:51:59.677 KVC[8450:699448] 用户名:阳;地址:北京站
2015-10-10 13:51:59.677 KVC[8450:699448] {
address = "<Address: 0x7faf33439680>";
userName = "\U9633";
}
Swift
常用KVC操作方法
动态设置
setValue(value: AnyObject?, forKey key: String)
(用于简单路径)setValue(value: AnyObject?, forKeyPath keyPath: String)
(用于复合路径,例如Person有一个Account类型的属性,那么person.account就是一个复合属性)setValuesForKeysWithDictionary(keyedValues: [String : AnyObject])
;(用于字典传入,大数据赋值)
动态读取
valueForKey(key: String) -> AnyObject?
(简单路径取值)valueForKeyPath(keyPath: String) -> AnyObject?
(复合路径取值)dictionaryWithValuesForKeys(keys: [String]) -> [String : AnyObject]
(批量取值,可以一致性提取多个数据)
例子
模型数据
User.swift
import Foundation
/// 用户
class User: NSObject {
/// 用户名
dynamic var userName:String?
override init() {
userName = ""
}
}
XCTestCase测试
//
// KVCTests.swift
// KVCTests
//
// Created by yangjun on 15/10/10.
// Copyright © 2015年 六月. All rights reserved.
//
import XCTest
@testable import KVC
/// KVC测试
class KVCTests: XCTestCase {
/// 用户
var user:User!;
// MARK: 开始
override func setUp() {
super.setUp()
self.user = User()
}
func testExample() {
// 简单路径
self.user.setValue("yangj", forKey:"userName") // 传值
var value = self.user.valueForKey("userName") as? String // 取值
print("\(value)")
// 复合路径
self.user.setValue("阳", forKeyPath: "userName")
value = self.user.valueForKeyPath("userName") as? String
print("\(value)")
// 字典
let dict = ["userName":"阳君"]
self.user.setValuesForKeysWithDictionary(dict)
let dictOut = self.user.dictionaryWithValuesForKeys(["userName"])
print("\(dictOut)")
}
}
输出
Optional("yangj")
Optional("阳")
["userName": 阳君]