为啥 Swift 中的字符串、数组和字典改为值类型

Posted

技术标签:

【中文标题】为啥 Swift 中的字符串、数组和字典改为值类型【英文标题】:why string, array and dictionary in Swift changed to value type为什么 Swift 中的字符串、数组和字典改为值类型 【发布时间】:2015-05-28 23:51:19 【问题描述】:

在 Objc 字符串中,数组和字典都是引用类型,而在 Swift 中它们都是值类型。

    我想弄清楚幕后的原因是什么,据我了解,无论是引用类型还是值类型,对象都存在于 Objc 和 Swift 中的堆中。

    改变是为了让编码更容易吗?即,如果它是引用类型,那么指向对象的指针可能不是 nil,因此需要检查指针和对象不是 nil 以访问对象。而如果它是值类型,那么只需要检查对象本身吗?

    但是在内存分配方面,值类型和引用类型是一样的,对吧?两者都分配了相同大小的内存?

谢谢

【问题讨论】:

【参考方案1】:

我不知道,这是否是它背后的真实想法,但有一个历史观点:

一开始,当您更改其中的项目时,数组副本的行为是通过引用进行的。当您更改数组的长度时,它按值运行。他们这样做是出于性能原因(更少的数组副本)。但是当然这是,呃,我怎么能礼貌地表达,呃,用斯威夫特很难,呃,我们称之为“如果你能赢得一些表现,你可能永远不需要”的方法。 .有人称其为写时复制,并没有什么更智能,因为 COW 是透明的,而这种行为是不透明的。典型的 Swift 措辞:使用流行语,用它的方式,它适合 Swift,不关心正确性。

后来数组得到了完整的复制行为,不那么令人困惑。 (你记得,Swift 是为了可读性。显然,在 Swift 的概念中,可读性意味着“更少的字符可以阅读”,但并不意味着“更好地理解”。典型的 Swift 措辞:使用流行语,按方式使用,它适合 Swift,不关心正确性。我已经提到了吗?)

所以,我猜它仍然是性能加上可理解的行为可能会导致性能下降。 (如果源数组是不可变的,您将更好地知道代码中何时需要副本,并且您仍然可以这样做并且您从 Cocoa 获得 0 操作。)当然,他们可以说:“好的,按值是一个错误,我们改变了这一点。”但他们永远不会说。

然而,现在 Swift 中的数组表现一致。 Swift 的一大进步!也许你可以在阳光明媚的日子里称它为一种编程语言。

【讨论】:

这并不能回答所提出的任何问题。这只是你对 Swift 的总体看法。【参考方案2】:

Objective-C 中的数组、字典等通常是可变的。这意味着当我将一个数组传递给另一个方法,然后在另一个方法后面修改该数组时,会发生令人惊讶的(委婉地说)行为。

通过创建数组、字典等值类型,可以避免这种令人惊讶的行为。当你收到一个 Swift 数组时,你就知道没有人会在你背后修改它。可以在背后修改的对象是问题的主要来源。

实际上,Swift 编译器会尽可能避免不必要的复制。所以即使它说一个数组是官方复制的,也不代表它是真的复制的。

【讨论】:

我从未陷入过问题的主要根源。但是,数组的副本有多深?为什么它是“主要问题来源”,当数组发生变化时,而不是“主要问题来源”,当该数组中的一个项目改变了它的一个属性时?以及为什么他们在 Swift 团队中长期认为改变 item 不是“主要问题来源”而改变数组长度是“主要问题来源”,为什么他们改变了看法? 我基本同意你的观点,但我想,在 Obj-C 中,只要没有其他选择,开发人员有责任不传递/请求可变对象。非可变对象不能在调用者背后修改,所以从这个角度来看,你的逻辑对我来说有点坏了。 我的逻辑观点很简单,就是“在我背后修改”在实践中不会造成严重的问题。这是 Swift 的“我们解决了仅在理论上存在的问题”的特性之一。你在生产代码中只遇到过一次这个问题吗?所以一开始有一个快速而肮脏(不一致)的解决方案来解决没有问题,现在我们有一个一致的解决方案来解决没有问题,这有时会让思考变得更加困难,并且会有性能打印。 对于任何有兴趣的人来说,他们解决这个“没有问题”的原因,即使你自己没有遇到它可能是其他人(框架开发人员)已经投入很多工作来保护您免受这些最有可能发生的错误的影响。这是我在观看 WWDC15 的“使用 Swift 中的值类型构建更好的应用程序”时得到的印象。 @SandeepAggarwal developer.apple.com/library/content/documentation/Swift/… 最后一个主题“分配和复制...”见注释【参考方案3】:

Swift 团队在官方开发者论坛上非常活跃。因此,我假设由于您没有在那里询问,因此您对社区对更改含义的更广泛“意义”感到好奇,而不是技术实施细节。如果您想确切了解“为什么”,请去问他们:)

对我来说最有意义的解释是对象应该负责响应和更新应用程序的状态。值应该是您的应用程序的状态。换句话说,数组、字符串或字典(以及其他值类型)永远不应该负责响应用户输入或网络输入或错误条件等。对象处理这些并将结果数据存储到这些值中。

Swift 中有一个很酷的特性,它使复杂的值类型(如字典或自定义类型如 Person,而不是简单的 Float)更可行,即值类型可以封装规则和逻辑,因为它们可以具有职能。如果我将值类型Person 编写为结构,那么 Person 结构可以具有用于更新由于婚姻等原因而产生的名称的功能。这仅与数据有关,与 /managing/ 状态无关。对象仍将决定何时和为什么更新人的姓名,但如何安全/可测试地执行此操作的业务逻辑可以包含在值类型本身中。因此,为您提供了一种增加隔离度和降低复杂性的好方法。

【讨论】:

【参考方案4】:

除了前面的答案之外,共享基于引用的集合类型还需要考虑多线程问题,我们不必担心共享基于值的类型的实例和具有写时复制行为。多核在 ios 设备上也越来越多,因此成为 Swift 语言开发者需要考虑的问题。

【讨论】:

以上是关于为啥 Swift 中的字符串、数组和字典改为值类型的主要内容,如果未能解决你的问题,请参考以下文章

Swift4.2~数组和字典(Array, Dictionary)基本类型转换

Swift中的元组,数组,字典

在iOS9 swift中的字典类型中添加数组作为键和值

Swift:为啥字典中的键类型必须是 Hashable 类型 [重复]

swift实现遍历嵌套字典并修改其中的值

swift实现遍历嵌套字典并修改其中的值