通过引用定义类型和副本的 Swift 集合?

Posted

技术标签:

【中文标题】通过引用定义类型和副本的 Swift 集合?【英文标题】:Swift collection that has defined types and copies by reference? 【发布时间】:2021-12-14 04:57:25 【问题描述】:

在将旧项目从 Objective-C 转换为 Swift 时,我大多能够使用 Dictionary 代替 NSMutableDictionary。但在某些情况下,按值复制字典会很麻烦或使用大量额外内存。

我想我可以简单地将一些 Dictionary 集合更改为 NSMutableDictionary 以使它们成为按值复制的对象,但我看不到指定键和值类型的方法。这适用于 Objective-C:

NSMutableDictionary<NSString*, NSString*> *dict = [NSMutableDictionary dictionary];

但这会在 Swift 中给出错误“无法专门化非泛型类型 'NSMutableDictionary'”:

let dict: NSMutableDictionary<String, String> = [:]

有没有办法指定类型,这样我就不必不断地转换从字典中得到的值?

或者,是否有另一种集合对象支持键和值类型,如Dictionary,但通过引用复制,如NSMutableDictionary

更新

我尝试按照下面评论中的建议使用NSMapTable。这缺少NSDictionary 的一些功能(例如,它不符合IteratorProtocol),所以我创建了一个子类来尝试替换Dictionary。然后我遇到了使我的子类泛型的问题,因为 Swift 和 Objective-C 对此有不同的支持。

因为这要么需要大量转换(或者为我想要存储在字典中的每种类型的数据创建一个不同的子类),因此我尝试只使用 NSMutableDictionary 并在我读取它们时转换所有值。不幸的是,经过所有这些工作,与使用 Dictionary 相比,我看不出内存使用有任何差异。

所以我想拥有按值复制的集合并不是我的问题。这不应该是因为我从来没有保留任何东西很长时间,但是直到我从 Objective-C 迁移之前我没有看到这些内存问题。我将不得不进行更多测试并探索其他解决方案。

【问题讨论】:

不完全是'通过引用复制,但NSMapTable 可以持有弱引用。这是来自NSHipster的更多信息 【参考方案1】:

Objective-C 规范:

NSMutableDictionary<NSString*, NSString*>

不是泛型的真正实现。它只是向编译器提示字典将包含字符串作为键和值,并且编译器可以在编译时告诉您是否犯了一个简单的错误。

归根结底,在运行时没有什么可以强制执行这些类型规范。 NSDictionary(可变与否)将有 id 作为键,id 作为值,运行时格式不会改变。这就是为什么你可以不使用[NSMutableDictionary dictionary] 来初始化所有NSDictionaries... 类型规范仅在编译时才有意义。

相比之下,当您在 Swift 中使用相同的语法时,例如 Dictionary&lt;String, Int&gt;,您正在使用真正的泛型。字典的运行时表示可能会根据您使用的键和值类型而改变。

换句话说,尽管它们在语法上相似,但在 Objective-C 和 Swift 中的 &lt;type, type&gt; 构造意味着非常不同的东西。

在 Swift 眼中,NSDictionary(可变与否)只是一个 NSObject,就像其他所有 NSObject 一样,所以 NSDictionary 是对泛型类型规范语法的无意义使用——你重新说你想使用非泛型类型的泛型(因此出现错误)。

没有 Swift 语法(据我所知)可以让您在 NSDictionariesNSArrays 之类的东西中指定您希望代表 NSObject 的类型。您将不得不使用强制转换。

即使在 Objective-C 中,类型规范也没有任何意义,并且可以在其中挤入不属于的东西。考虑:

 NSDictionary<NSString *, NSString *> *myDictionary = [NSMutableDictionary dictionary];
 ((NSMutableDictionary *)myDictionary)[[NSNumber numberWithInt: 3]] = [NSURL URLWithString: @"http://www.apple.com"];

在这里,我声明 dict 使用字符串,然后插入一个数字和一个 URL。防止这种情况的唯一方法是检查每个键和值的类型,即进行类型转换(或至少进行类型检查)。大多数人的代码不会这样做,因为这会很痛苦,但这是获得真正安全性的唯一方法。

相比之下,Swift 更注重安全。这是 Swift 和 Objective-C 之间的决定性差异之一。因此,如果您坚持使用“不安全”的 Objective-C 类型,您必须经历痛苦。

【讨论】:

以上是关于通过引用定义类型和副本的 Swift 集合?的主要内容,如果未能解决你的问题,请参考以下文章

swift基本语法——数据类型

Swift 值类型 和 引用类型的区别

Swift 值类型 和 引用类型的区别

Swift - 值类型和引用类型的区别

如何引用在不同的 .swift 文件中定义的数据类型?

如何引用在不同的 .swift 文件中定义的数据类型?