键值编码 (KeyValueCoding)

Swift 5.10, 5.9, 5.8, 5.7 Platforms: iOS, macOS, tvOS, visionOS, watchOS Swift Package Manager: compatible Build Codecov Swift Doc Coverage

Donate

KeyValueCoding 提供了一种机制,允许你通过字符串键或键路径间接访问纯 Swift structclass 实例的属性。

入门

基础

访问实例属性值的 KeyValueCoding 协议的基本方法是通过字符串键或键路径进行下标操作。 为了使你的类型符合键值编码,只需从该协议继承即可,例如:

import KeyValueCoding

struct Resolution: KeyValueCoding {
  let width: Int
  let height: Int
}

var resolution = Resolution(width: 640, height: 480)

resolution["width"] = 1920
resolution["height"] = 1080

print(resolution) // Prints: Resolution(width: 1920, height: 1080)

注意:实例变量必须声明为 var,否则会出现以下错误:Cannot use mutating getter on immutable value (无法对不可变值使用可变 getter)。

同样适用于键路径:

resolution[\Resolution.width] = 2560
resolution[\Resolution.height] = 1440

print(resolution) // Prints: Resolution(width: 2560, height: 1440)

您还可以以相同的方式读取属性值:

if let width: Int = resolution[\Resolution.width], let height: Int = resolution[\Resolution.height] {
  print("\(width)x\(height)") // Prints: 2560x1440
}

以下属性可以以一致的方式用于类和结构体,包括:

但是有一些 **限制**:

关系

KeyValueCoding 可以通过字符串键(“relationship.property”)或键路径访问关系属性,例如:

import KeyValueCoding

struct Resolution {
  let width: Int
  let height: Int
}

class VideoMode: KeyValueCoding {
  let name: String
  let resolution: Resolution
  
  init(name: String, resolution: Resolution) {
    self.name = name
    self.resolution = resolution
  }
}

var videoMode = VideoMode(name: "HD", resolution: Resolution(width: 1920, height: 1080))
print("\(videoMode.name) - \(videoMode.resolution.width)x\(videoMode.resolution.height)")
// Prints: HD - 1920x1080

videoMode[\VideoMode.name] = "4K"
videoMode[\VideoMode.resolution.width] = 3840
videoMode[\VideoMode.resolution.height] = 2160
print("\(videoMode.name) - \(videoMode.resolution.width)x\(videoMode.resolution.height)")
// Prints: 4K - 3840x2160

注意:您的父实例可以访问其子级的属性,而无需子级符合 KeyValueCoding 协议。

类继承

从继承类继承的属性也可以通过 KeyValueCoding 协议访问。

import KeyValueCoding

class Mode {
  let name: String
  
  init(name: String) {
    self.name = name
  }
}

class VideoMode: Mode, KeyValueCoding {
  let frameRate: Int
  
  init(name: String, frameRate: Int) {
    self.frameRate = frameRate
    super.init(name: name)
  }
}

var videoMode = VideoMode(name: "HD", frameRate: 30)
print("\(videoMode.name) - \(videoMode.frameRate)fps")
// Prints: HD - 30fps

videoMode[\VideoMode.name] = "4K"
videoMode[\VideoMode.frameRate] = 25
print("\(videoMode.name) - \(videoMode.frameRate)fps")
// Prints: 4K - 25fps

协议

您可以从 KeyValueCoding 继承任何协议,然后该协议的所有实例都将可以访问以读取和写入其属性。

import KeyValueCoding

protocol Size: KeyValueCoding {
  var width: Int { get }
  var height: Int { get }
}

struct Resolution: Size {
  let width: Int
  let height: Int
}

var resolution: Size = Resolution(width: 1920, height: 1080)
print(resolution)
// Prints: Resolution(width: 1920, height: 1080)

resolution[\Resolution.width] = 3840
resolution[\Resolution.height] = 2160

if let width: Int = resolution[\Resolution.width], let height: Int = resolution[\Resolution.height] {
  print("\(width)x\(height)")
  // Prints: 3840x2160
}

高级函数

此外,您可以纯粹使用 API 函数来获取和设置实例属性的值,而 **无需采用** KeyValueCoding 协议。

import KeyValueCoding

struct Resolution {
  let width: Int
  let height: Int
}

var resolution = Resolution(width: 1920, height: 1080)
print(resolution)
// Prints: Resolution(width: 1920, height: 1080)

swift_setValue(3840, to: &resolution, keyPath: \Resolution.width)
swift_setValue(2160, to: &resolution, keyPath: \Resolution.height)

if let width = swift_value(of: &resolution, keyPath: \Resolution.width) as? Int,
    let height = swift_value(of: &resolution, keyPath: \Resolution.height) as? Int
{
  print("\(width)x\(height)")
  // Prints: 3840x2160
}

方法

采用 KeyValueCoding 协议的 structclass 的 Swift 实例对其属性符合键值编码,并且可以通过基本下标 [key][keyPath] 进行寻址。

元数据 (metadata)

返回实例的元数据,其中包括其类型、种类、大小和可访问属性的列表。

import KeyValueCoding 

struct Resolution: KeyValueCoding {
  let width: Int
  let height: Int
}

let resolution = Resolution(width: 1920, height: 1080)
print(resolution.metadata)

打印

Metadata(type: Resolution, kind: .struct, size: 16, properties: [
  Property(name: 'width', isStrong: true, isLazy: false, isVar: false, offset: 0), 
  Property(name: 'height', isStrong: true, isLazy: false, isVar: false, offset: 8)
])

[键] ( [key] )

获取并设置由字符串键标识的属性的值。

import KeyValueCoding

struct Resolution: KeyValueCoding {
  let width: Int
  let height: Int
}

var resolution = Resolution(width: 1920, height: 1080)

resolution["width"] = 2048

if let width: Int = resolution["width"] {
  print(width) // Prints: 2048
}

[键路径] ( [keyPath] )

获取并设置由键路径标识的属性的值。

import KeyValueCoding

struct Resolution: KeyValueCoding {
  let width: Int
  let height: Int
}

var resolution = Resolution(width: 1920, height: 1080)

resolution[\Resolution.width] = 2048

if let width: Int = resolution[\Resolution.width] {
  print(width) // Prints: 2048
}

API

全局 API 函数,用于设置、获取和检索来自任何实例或类型的元数据信息,**即使没有采用** KeyValueCoding 协议。

swift_metadata

返回实例或类型的元数据,其中包括其 typekindsize 和可访问的 properties

import KeyValueCoding

struct Resolution {
  let width: Int
  let height: Int
}

let resolution = Resolution(width: 1920, height: 1080)
    
var metadata = swift_metadata(of: resolution)
// OR
metadata = swift_metadata(of: type(of: resolution))
// OR
metadata = swift_metadata(of: Resolution.self)

print(metadata)

打印

Metadata(type: Resolution, kind: .struct, size: 16, properties: [
 Property(name: 'width', isStrong: true, isLazy: false, isVar: false, offset: 0), 
 Property(name: 'height', isStrong: true, isLazy: false, isVar: false, offset: 8)
])

swift_value

返回由给定字符串键或键路径标识的实例属性的值。

import KeyValueCoding

struct Resolution {
  let width: Int
  let height: Int
}

var resolution = Resolution(width: 1920, height: 1080)
    
if let width = swift_value(of: &resolution, key: "width") as? Int {
  print(width) // Prints: 1920
}
// OR   
if let width = swift_value(of: &resolution, keyPath: \Resolution.width) as? Int {
  print(width) // Prints: 1920
}

swift_setValue

将由给定字符串键或键路径指定的实例的属性设置为给定的值。

import KeyValueCoding

struct Resolution {
  let width: Int
  let height: Int
}

var resolution = Resolution(width: 1920, height: 1080)
    
swift_setValue(2048, to: &resolution, key: "width")
// OR
swift_setValue(2048, to: &resolution, keyPath: \Resolution.width)
    
print(resolution) // Prints: Resolution(width: 2048, height: 1080)

安装

XCode

  1. 选择 Xcode > File > Add Packages... (Xcode > 文件 > 添加包...)
  2. 添加包存储库: https://github.com/ikhvorost/KeyValueCoding.git
  3. 在源文件中导入包: import KeyValueCoding

Swift 包 (Swift Package)

KeyValueCoding 包依赖项添加到您的 Package.swift 文件中

let package = Package(
    ...
    dependencies: [
        .package(url: "https://github.com/ikhvorost/KeyValueCoding.git", from: "1.0.0")
    ],
    targets: [
        .target(name: "YourPackage",
            dependencies: [
                .product(name: "KeyValueCoding", package: "KeyValueCoding")
            ]
        ),
        ...
    ...
)

许可

KeyValueCoding 在 MIT 许可证下可用。 有关更多信息,请参见 LICENSE 文件。

Donate