Arrays 数组的副本正在更改原始数组
我有两个控制器。第一个控制器从服务器加载一个列表,并创建一个自定义对象列表,Arrays 数组的副本正在更改原始数组,arrays,swift,Arrays,Swift,我有两个控制器。第一个控制器从服务器加载一个列表,并创建一个自定义对象列表,WordList class WordList { let name: String let releaseDate: Date var words: [String] let multiplier: Int ... } 在第一个屏幕上,用户可以在继续之前选择列表。在下一个控制器上,从随机列表中选择一个随机字。单词出现后,在用户与其交互时将其删除。一旦用户不进行交互,就会选择
WordList
class WordList {
let name: String
let releaseDate: Date
var words: [String]
let multiplier: Int
...
}
在第一个屏幕上,用户可以在继续之前选择列表。在下一个控制器上,从随机列表中选择一个随机字。单词出现后,在用户与其交互时将其删除。一旦用户不进行交互,就会选择一个新词,直到没有新词为止。如果我回到主控制器并选择与我刚才使用的相同的列表,该列表将为空。这就是我发送所选项目的方式
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? WordController {
var wordLists = [WordList]()
for index in tableView.indexPathsForSelectedRows! {
wordLists.append(lists[index.row]) // lists is a class property that is a WordList array.
}
// This was my first attempt. Values were copied but removed from this controller.
// for wordList in wordLists {
// destination.wordLists.append(wordList)
// }
// destination.wordLists = wordLists
// This was my second attempt. Values were also copied but removed from this controller.
destination.wordLists.append(contentsOf: wordLists)
}
}
我知道我必须传递列表的引用,而不是实际复制它,但我不认为如果我通过复制第一个数组的值来填充第二个数组,它会是这样工作的
除了在用户每次返回屏幕时重新加载第一个控制器外,我如何进行此工作,以便用户可以重用已清除的列表?您说过:
我明白我必须传递列表的引用,而不是实际复制它
不,您正在传递一个新数组
。。。但我不认为如果我通过复制第一个数组中的值来填充第二个数组,它会是这样工作的
不幸的是,您没有从第一个数组“复制值”,而是将WordList
引用从第一个数组复制到第二个数组中。总之,问题不在于数组
,它是一种值类型,而在于单词列表
,它是一种引用类型
WordList
是一种引用类型,因为它是类
。因此,当您从一个数组中引用WordList
并将其添加到另一个数组中时,第二个数组仍将引用相同的WordList
实例
如果不希望对其他阵列实例的操作影响原始实例,可以:
- 将
从引用类型(aWordList
)更改为值类型(aclass
):struct
- 如果确实需要使用
,请编写自己的类
方法返回新实例。例如,您可以遵循copy
并编写NSCopying
: 然后,在构建新阵列时,将副本而不是引用附加到原始实例:副本(使用:)
for index in tableView.indexPathsForSelectedRows! { wordLists.append(lists[index.row].copy() as! WordList) }
如果您不喜欢
NSCopying
为copy
引入了笨拙的Any
返回类型,您也可以定义您的copy
方法,甚至编写自己的Copying
协议,例如:
protocol Copying {
associatedtype ObjectType = Self
func copy() -> ObjectType
}
extension WordList: Copying {
func copy() -> WordList {
return WordList(name: name, releaseDate: releaseDate, words: words, multiplier: multiplier)
}
}
然后您可以执行以下操作,无需强制转换:
for index in tableView.indexPathsForSelectedRows! {
wordLists.append(lists[index.row].copy())
}
你说:
我明白我必须传递列表的引用,而不是实际复制它
不,您正在传递一个新数组
。。。但我不认为如果我通过复制第一个数组中的值来填充第二个数组,它会是这样工作的
不幸的是,您没有从第一个数组“复制值”,而是将WordList
引用从第一个数组复制到第二个数组中。总之,问题不在于数组
,它是一种值类型,而在于单词列表
,它是一种引用类型
WordList
是一种引用类型,因为它是类
。因此,当您从一个数组中引用WordList
并将其添加到另一个数组中时,第二个数组仍将引用相同的WordList
实例
如果不希望对其他阵列实例的操作影响原始实例,可以:
- 将
从引用类型(aWordList
)更改为值类型(aclass
):struct
- 如果确实需要使用
,请编写自己的类
方法返回新实例。例如,您可以遵循copy
并编写NSCopying
: 然后,在构建新阵列时,将副本而不是引用附加到原始实例:副本(使用:)
for index in tableView.indexPathsForSelectedRows! { wordLists.append(lists[index.row].copy() as! WordList) }
如果您不喜欢
NSCopying
为copy
引入了笨拙的Any
返回类型,您也可以定义您的copy
方法,甚至编写自己的Copying
协议,例如:
protocol Copying {
associatedtype ObjectType = Self
func copy() -> ObjectType
}
extension WordList: Copying {
func copy() -> WordList {
return WordList(name: name, releaseDate: releaseDate, words: words, multiplier: multiplier)
}
}
然后您可以执行以下操作,无需强制转换:
for index in tableView.indexPathsForSelectedRows! {
wordLists.append(lists[index.row].copy())
}
我会避免nscopy,因为该协议的唯一要点是支持NSObject.copy。它还引入了任何不需要的类型。
NSCopying
协议没有只针对NSObject
子类的要求(例如,如果它真的只针对NSObject
,它将有NSObjectProtocol
要求)。但是我承认,我发现NSCopying
对任何
返回类型的使用有点不简洁。您始终可以定义自己的复制协议。我只是讨厌引入另一个很大程度上重复的协议/方法,而现有的协议就是为了这个目的而设计的。实际上不需要复制协议。协议存在的唯一原因是NSObject
有一个copy
方法,当实例不符合NSCopying
时抛出异常,否则调用copy(withZone:)
。最初的原因可能是需要copy
属性,但在Swift中,我只声明一个复制构造函数(initalizer)。我同意您不需要协议(因此我的评论是您“可以定义copy
方法”),但这似乎是最佳实践,IMHO,为这些常见的抽象模式定义协议。它在国际文传电讯组织中明确了意图