Swift 计算属性返回底层数组的副本

Posted

技术标签:

【中文标题】Swift 计算属性返回底层数组的副本【英文标题】:Swift computed property to return copy of underlying array 【发布时间】:2014-09-11 23:40:53 【问题描述】:

我有一个用 Objective-C 编写的模型类,我正在将其转换为 Swift。它在内部包含一个NSMutableArray,但getter 的方法签名以及实际返回值是NSArray。调用时,它会创建一个不可变副本以返回。

本质上,我希望调用者能够迭代/检查容器,但不能修改它。我有这个测试sn-p:

class Container 
    internal var myItems = [String]()

    func sayHello() 
        "I have: \(myItems)"
    


let cont = Container()

cont.myItems.append("Neat") // ["Neat"]

cont.sayHello() // This causes sayHello() to print: "I have: [Neat]"

var isThisACopy = cont.myItems

isThisACopy.append("Huh") // ["Neat", "Huh"]

cont.sayHello() // This ALSO causes sayHello() to print: "I have: [Neat]"

我一直在尝试找到一种方法来覆盖 myItems 的 getter,以便它返回一个不可变的副本,但似乎无法确定如何。

尝试 #1

这会产生编译器错误:Function produces expected type '_ArrayBuffer<(String)>'; did you mean to call it with '()'?

internal var myItems = [String]() 
    var copy = [String]()
    for item in ...  // What to use in the ...?
        copy.append(item)
    
    return copy

尝试 #2

这也会产生编译器错误,因为我(可以理解)重新定义生成的 getter Invalid redeclaration of 'myItems()'

internal func myItems() -> [String] 
    var copy = [String]()
    for item in myItems 
        copy.append(item)
    
    return copy

【问题讨论】:

【参考方案1】:

试试这个:

class Container 
    private var _myItems: [String] = ["hello"]
    internal var myItems: [String] 
        return _myItems
    


let cont = Container()
cont.myItems.append("Neat") //not allowed

它使用私有存储属性和返回不可变副本的计算属性。存储属性不能使用自定义 getter。

【讨论】:

这是我在尝试 #1 中遇到的另一个问题:self.myItems 的访问会产生无限递归,因为 getter 正在调用 自身 我不确定您的实际用例是什么,但您可以使用 internal var myItems: [String] return self.myItems 它返回 myItems 的不可变副本 否 - 这也有同样的问题。使用self.myItems 调用getter,它使用self.myItems,调用getter... Womp womp。 :-) 你可以添加一个公共计算属性(使用不同的名称)返回该数组的不可变副本吗? 我熟悉下划线方法 - 这会起作用。使用动态吸气剂时没有“直接访问”,有点令人遗憾。我想这不是他们的工作方式。谢谢!【参考方案2】:

expose mutable properties as immutable 的更好方法:

class Container 
    private (set) internal var myItems: [String]

【讨论】:

您能解释一下编译器如何确定列表本身应该是不可变的吗?我可以理解private(set) 阻止调用者将myItems 指向一个新列表,但我不太明白它是如何知道(没有任何let 出现)该列表也是不可变的? @CraigOtis 与@connor 的答案中的myItems 相同——只读具有与常量相同的语义。

以上是关于Swift 计算属性返回底层数组的副本的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 4 中创建类的非零属性数组

Swift-存储属性,计算属性,类属性

《从零开始学Swift》学习笔记(Day 50)——扩展计算属性方法

用返回非可选值的计算属性覆盖返回可选值的计算属性

从0开始学Swift笔记整理

编辑从计算属性返回的数组中的对象