通过引用定义类型和副本的 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<String, Int>
,您正在使用真正的泛型。字典的运行时表示可能会根据您使用的键和值类型而改变。
换句话说,尽管它们在语法上相似,但在 Objective-C 和 Swift 中的 <type, type>
构造意味着非常不同的东西。
在 Swift 眼中,NSDictionary
(可变与否)只是一个 NSObject
,就像其他所有 NSObject
一样,所以 NSDictionary
没有 Swift 语法(据我所知)可以让您在 NSDictionaries
和 NSArrays
之类的东西中指定您希望代表 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 集合?的主要内容,如果未能解决你的问题,请参考以下文章