Swift 中的动态成员查找

这篇具有很好参考价值的文章主要介绍了Swift 中的动态成员查找。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Swift 中的动态成员查找,Swift,swift,ios,原力计划

Swift 中的动态成员查找,Swift,swift,ios,原力计划

前言

在 Swift 中,动态成员查找是一种允许在编译时未知成员的情况下,通过字符串名称来访问属性和方法的机制。这在需要与动态数据进行交互,或者在某些情况下进行元编程时非常有用。动态成员查找通过 Swift 的 @dynamicMemberLookup 特性实现。

Glassfy:简化构建、管理和推广应用内购买。从订阅管理 SDK 到付费墙等完整的货币化工具。立即免费构建。

基础介绍

假设我们正在开发一个提供缓存功能的类型,并将其建模为名为 Cache 的结构体。

struct Cache {
    var storage: [String: Data] = [:]
}

为了访问缓存的数据,我们调用存储属性的下标,该存储属性是 Dictionary 类型提供的。

var cache = Cache()
let profile = cache.storage["profile"]

在这里没有什么特别之处。我们像以前一样通过 Dictionary 类型的下标访问字典。让我们看看如何使用 @dynamicMemberLookup 属性改进 Cache 类型的 API。

@dynamicMemberLookup
struct Cache {
    private var storage: [String: Data] = [:]

    subscript(dynamicMember key: String) -> Data? {
        storage[key]
    }
}

如上例所示,我们使用 @dynamicMemberLookup 属性标记了 Cache 类型。我们必须实现具有 dynamicMember 参数并返回我们需要的任何内容的下标。

var cache = Cache()
let profile = cache.profile

现在,我们可以更方便地访问 Cache 类型的配置文件数据。我们的 API 的使用者可能会认为配置文件是 Cache 类型的属性,但事实并非如此。

此特性完全在运行时工作,并利用了在点符号后键入的任何属性名称来访问 Cache 类型的下标,该下标具有 dynamicMember 参数。

整个逻辑在运行时运行,编译期间的结果是不确定的。在运行时,完全可以决定应该从下标返回哪些数据以及如何处理 dynamicMember 参数。

基础示例

以下是在 Swift 中使用动态成员查找的一些基本示例:

1. 定义一个动态成员访问类:

@dynamicMemberLookup
struct DynamicMemberExample {
    subscript(dynamicMember member: String) -> String {
        return "Accessed dynamic member: \(member)"
    }
}

// 创建实例
let dynamicObject = DynamicMemberExample()

// 使用动态成员查找
print(dynamicObject.someProperty) // 输出: Accessed dynamic member: someProperty
print(dynamicObject.someMethod()) // 输出: Accessed dynamic member: someMethod()

在上面的示例中,我们定义了一个结构体 DynamicMemberExample,使用 @dynamicMemberLookup 注解。这允许我们通过下标方法 subscript(dynamicMember:) 来实现对动态成员的访问。然后我们可以像使用普通成员一样使用动态成员。

2. 访问嵌套动态成员:

@dynamicMemberLookup
struct NestedDynamicMemberExample {
    struct Inner {
        subscript(dynamicMember member: String) -> String {
            return "Accessed dynamic member: \(member)"
        }
    }
    
    var inner = Inner()
}

// 创建实例
let nestedDynamicObject = NestedDynamicMemberExample()

// 使用动态成员查找
print(nestedDynamicObject.inner.someProperty) // 输出: Accessed dynamic member: someProperty

在这个示例中,我们在一个嵌套的结构体 Inner 中实现了动态成员查找。然后我们可以通过外部结构体 NestedDynamicMemberExample 的实例访问嵌套结构体的动态成员。

动态成员查找是一种强大的特性,但也要注意,它通常会损失一些类型安全性,因为在编译时不会检查动态成员的存在。使用动态成员查找时,请确保仔细考虑潜在的风险。

请注意,动态成员查找在 Swift 5.1 及更高版本中可用。如果在更早的版本中使用动态成员查找,可能需要进行适当的版本升级。

使用 KeyPath 的编译时安全性

我们唯一能找到的缺点是缺乏编译时安全性。我们可以将 Cache 类型视为代码中键入的任何属性名称。幸运的是,@dynamicMemberLookup 下标的参数不仅可以是 String 类型,还可以是 KeyPath 类型。

@dynamicMemberLookup
final class Store\<State, Action>: ObservableObject {
typealias ReduceFunction = (State, Action) -> State

    @Published private var state: State
    private let reduce: ReduceFunction

    init(
        initialState state: State,
        reduce: @escaping ReduceFunction
    ) {
        self.state = state
        self.reduce = reduce
    }

    subscript<T>(dynamicMember keyPath: KeyPath<State, T>) -> T {
        state[keyPath: keyPath]
    }

    func send(_ action: Action) {
        state = reduce(state, action)
    }

}

如上例所示,我们定义了接受强类型 KeyPath 实例的 dynamicMember 参数下标。在这种情况下,我们允许 State 类型的 KeyPath,这有助于我们获得编译时安全性。因为每当我们传递与 State 类型无关的错误 KeyPath 时,编译器都会显示错误。

struct State {
var products: \[String] = \[]
var isLoading = false
}

enum Action {
case fetch
}

let store: Store\<State, Action> = .init(initialState: .init()) { state, action in
var state = state
switch action {
case .fetch:
state.isLoading = true
}
return state
}

print(store.isLoading)
print(store.products)
print(store.favorites) // Compiler error

在上例中,我们通过接受 KeyPath 的下标访问 Store 的私有 state 属性。这看起来与前面的例子类似,但在这种情况下,只要尝试访问 State 类型的不可用属性,编译器就会显示错误。

KeyPath 用法示例

当涉及到 Swift 中的 KeyPath 时,实际的代码示例可以更好地阐明其概念和用法。下面将详细介绍先前提到的示例代码,以更清晰地展示 KeyPath 的用途和编译时安全性。

首先,我们定义一个名为 Person 的结构体,表示一个人的姓名和年龄:

struct Person {
    let name: String
    let age: Int
}

接下来,我们创建一个 Person 实例,名为 person,并为其赋予姓名 “Alice” 和年龄 30:

let person = Person(name: "Alice", age: 30)

然后,我们使用 KeyPath 来引用 Person 实例的属性。我们创建了两个 KeyPath,一个用于访问姓名属性,另一个用于访问年龄属性:

let nameKeyPath = \Person.name
let ageKeyPath = \Person.age

使用 KeyPath,我们可以通过下标语法从 person 实例中访问属性值:

let personName = person[keyPath: nameKeyPath] // "Alice"
let personAge = person[keyPath: ageKeyPath]   // 30

在这里,我们通过 nameKeyPathageKeyPath 使用了 KeyPath,这是编译时类型安全的。如果我们尝试引用不存在的属性,编译器将在编译时捕获错误。

接下来,我们假设有一个数组 people 包含多个 Person 实例。我们可以使用 KeyPath 来对数组中的实例进行映射,以获取所有人的姓名和年龄:

let people = [
    Person(name: "Alice", age: 30),
    Person(name: "Bob", age: 25),
    Person(name: "Charlie", age: 28)
]

let names = people.map(\.name) // ["Alice", "Bob", "Charlie"]
let ages = people.map(\.age)   // [30, 25, 28]

这里,我们使用 KeyPath 进行了数组的映射,从每个 Person 实例中提取了姓名和年龄。

最后,我们可以使用 KeyPath 对数组中的实例按年龄进行排序:

let sortedPeople = people.sorted(by: \.age)

通过传递 KeyPathsorted(by:) 方法,我们可以按年龄对人员进行排序。

总之,KeyPath 是 Swift 中的一项强大特性,它提供了类型安全的属性和下标访问方式,可以在编译时捕获错误,并具有映射、排序等便利功能。通过理解 KeyPath 的概念和用法,可以写出更安全、更易读和更高效的 Swift 代码。

KeyPath 进阶使用示例

下面介绍一下关于 KeyPath 更多的用法和示例

1. 动态访问属性:

let propertyName = "name"
let dynamicKeyPath = \Person[keyPath: propertyName] // Error: Cannot convert value of type 'String' to expected key path type 'WritableKeyPath<Person, _>'

在这个示例中,我们尝试使用字符串变量创建一个动态的 KeyPath。然而,这将会导致编译错误,因为编译器需要在编译时确定 KeyPath 的类型。

2. 结合可选属性和 KeyPath:

struct Team {
    let captain: Person?
}

let team = Team(captain: Person(name: "David", age: 32))

if let captainName = team.captain?[keyPath: \.name] {
    print("Captain's name: \(captainName)")
} else {
    print("No captain assigned")
}

在这个示例中,我们定义了一个 Team 结构体,其中的 captain 属性是可选的 Person 实例。通过使用 KeyPath,我们可以在安全的情况下访问可选属性。

3. 动态 KeyPath 和字典:

let personKeyPath: KeyPath<Person, String> = \.name

let personDictionary = ["name": "Ella", "age": "28"]
if let name = personDictionary[keyPath: personKeyPath] {
    print("Name from dictionary: \(name)")
} else {
    print("Name not found in dictionary")
}

在这个示例中,我们将 KeyPath 与字典结合使用。尽管字典中的键和 KeyPath 类型可能不匹配,但在运行时可以使用动态 KeyPath 访问字典中的值。

KeyPath 是 Swift 中非常有用的一项功能,它提供了类型安全的属性和下标访问方法,可以在编译时捕获错误,还支持映射、排序和与其他类型的组合使用。理解 KeyPath 的概念和用法将为编写更清晰、更安全的 Swift 代码提供强大的工具。

总结

KeyPath 是 Swift 编程语言中的一项强大功能,它为属性和下标提供了类型安全的访问方式,提升了代码的可读性、可维护性和性能。通过使用 KeyPath,我们可以避免使用字符串进行属性访问,从而减少了因拼写错误而引发的问题。

通过深入理解 KeyPath 的概念和用法,开发者可以编写更具类型安全性、可读性和性能的 Swift 代码。这一功能不仅简化了代码编写,还为我们提供了一种更加优雅的方式来处理属性和下标的访问与操作。

我们学习了如何使用 @dynamicMemberLookup 属性改进特定类型的 API。虽然并不是每个类型都需要它,但可以谨慎使用它来改善 API。文章来源地址https://www.toymoban.com/news/detail-690779.html

到了这里,关于Swift 中的动态成员查找的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • IOS-生命周期-Swift

    App主要有五种状态,分别是: 未运行——Not running 应用程序没启动 未激活——Inactive 程序在前台运行,不过没有接收到事件。 一般每当应用要从一个状态切换到另一个不同的状态时,中途过渡会短暂停留在此状态。唯一在此状态停留时间比较长的情况是:当用户锁屏时,或

    2024年01月23日
    浏览(53)
  • iOS开发Swift-枚举

    枚举:一组相关的值定义了一个共同的类型,使你可以在代码中以类型安全的方式来使用这些值。 原始值:定义枚举时被预先填充的值。 (1)整数为原始值时,隐式赋值递增1。未设置原始值时,默认为0,之后递增1. (2)字符串为原始值,隐式赋值为枚举成员的名称。

    2024年02月11日
    浏览(56)
  • iOS开发Swift-函数

     (1)无参函数 (2)多参函数 (3)无返回值 (4)多重返回值 (5)可选元组返回类型(元组可以是nil) (6)隐式返回的函数 任一可以被写成一行return的函数,return(x) + for。 调用的时候: 方法名(for: 参数) (1)指定参数标签 (2)忽略参数标签 (3)默认参数值 (4)可变参数 一个可变参数可接受0个或多

    2024年02月11日
    浏览(55)
  • iOS开发Swift-控制流

    (1)复合匹配 (2)区间匹配 (3)元组匹配 (4)值绑定匹配 (5)where continue, break, fallthrough, return, throw continue: 停止本次循环,开始下次循环 break: 立即结束整个控制流。可以使用break忽略switch的分支。 fallthrough贯穿: switch中的case加入贯穿,case会穿透到下一个case/ default。

    2024年02月11日
    浏览(55)
  • iOS开发Swift-类型转换

    1.Int或Double转字符串 2.Double转Int(去掉小数点后面的) 3.Int转Double 4.向上转型 5.向下转型

    2024年02月09日
    浏览(50)
  • iOS开发系列--Swift语言

    Swift是苹果2014年推出的全新的编程语言,它继承了C语言、ObjC的特性,且克服了C语言的兼容性问题。Swift发展过程中不仅保留了ObjC很多语法特性,它也借鉴了多种现代化语言的特点,在其中你可以看到C#、Java、Javascript、Python等多种语言的影子。同时在2015年的WWDC上苹果还宣布

    2024年02月06日
    浏览(61)
  • 【教程】iOS Swift应用加固

    🔒 保护您的iOS应用免受恶意攻击!在本篇博客中,我们将介绍如何使用HTTPCORE DES加密来加固您的应用程序,并优化其安全性。通过以下步骤,您可以确保您的应用在运行过程中不会遭受数据泄露和未授权访问的风险。 本文将指导您如何通过改变编译方式、处理静态库、解决

    2024年01月23日
    浏览(50)
  • iOS开发Swift-基础部分

    系统可通过赋初始值进行自动推断。 平时可加可不加,同一行中有两句话必须加。 Int           UInt(最好不用) Double 64位 很大/高精度情况下使用 15位小数 Float 32位 对精度要求不高的情况下用 6位小数 十进制数   17 二进制 0b前缀 0b10001 八进制 0o前缀 0o21 十六进制 0x前缀

    2024年02月11日
    浏览(59)
  • iOS开发Swift-集合类型

    集合基本类型:数组 Array (有序), 集合 Set (无序不重复), 字典 Dictionary (无序键值对) (1)数组的表示 (2)创建空数组 (3)带值数组 (4)两数组相加创建数组 (5)字面量创造数组 (6)访问数组 (7)添加 (8)修改 (9)删除 (10)遍历 同时需要索引和值时: (1)集合的表示 (2)构造一个集合 (3)字面

    2024年02月11日
    浏览(104)
  • iOS_Swift高阶函数

    1.1 定义 高阶函数:higher-order function 如果一个函数: 接受一个或多个函数当作参数 把一个函数当作返回值 那么这个函数就被称做高阶函数。 例如: 看着不像是函数作为参数,是因为 Swift 尾随闭包 Trailing Closure 的特性。 Swift 允许当函数的最后一个参数是闭包时,可以以紧跟

    2024年02月04日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包