我通过ViewControllers之间的引用传递特定模型的数组。
如果我更改了数组中特定元素的任何值,它在所有ViewControllers中都能很好地反映,但是当我从该数组中删除一个元素时,它不会反映给其他控制器。
remove(at: )
函数是否创建新数组并引用另一个地址?
如果是这样,如何删除元素而不更改数组的地址,以便它可以在其他视图控制器上反映这种变化?
答案 0 :(得分:0)
Swift Arrays是值类型(具体来说,数组是结构),而不是引用类型,所以当你说“通过视图控制器之间的引用传递特定模型的数组”时,你就错了。您只能将Swift数组作为值传递。
与其他结构一样,数组具有复制修改语义。只要您更改数组本身,就会制作副本并对副本进行更改。
现在,在您的情况下,数组包含模拟对象的引用;更新模型对象时,更改对象本身,而不是数组中保存的引用,因此您可以看到所有视图控制器中反映的更改。
类比可能是将房屋添加到街道(改变街道本身)与改变街道上现有房屋的占用者之间的区别。
我建议您实现一个模型对象,该对象提供底层数组的抽象,以便您拥有更好的代码并避免数组引用的问题。
一种方法可能是:
struct MyModel {
let name: String
let size: Int
}
class MyData {
private var _models = [MyModel]()
var models: [MyModel] {
return _models
}
func insert(model: MyModel) {
self._models.append(model)
}
func removeModel(at: Int) {
guard at >= 0 && at < _models.count else {
return
}
self._models.remove(at: at)
}
}
虽然这并不理想,因为它仍然需要模型消费者知道底层数组中的索引。我更喜欢这样的事情:
struct MyModel: Hashable {
let name: String
let size: Int
}
class MyData {
private var _models = [MyModel]()
var models: [MyModel] {
return _models
}
func insert(model: MyModel) {
self._models.append(model)
}
func remove(model: MyModel) -> Bool {
if let index = self._models.index(of: model) {
_models.remove(at: index)
return true
} else {
return false
}
}
}
现在我不需要知道内部集合MyData
用于存储模型的内容。
答案 1 :(得分:0)
如果您需要通过引用传递数组(或任何其他值类型),您可以通过一个中间结构来管理您的间接。
[EDIT]已更改为使用Swift 4中提供的KeyPath。
// Generic class to hold a "weak" reference to a property from an object
// including properties that are valued types such as arrays, structs, etc.
// This is merely an encapsulation of Swift's native KeyPath feature
// to make the code a bit more readable and simpler to use
//
class ReferenceTo<ValueType> { var value:ValueType! { get { return nil} set {} } }
class Reference<OwnerType:AnyObject,ValueType>:ReferenceTo<ValueType>
{
internal weak var owner:OwnerType!
internal var property:ReferenceWritableKeyPath<OwnerType,ValueType>! = nil
internal var valueRef:KeyPath<OwnerType,ValueType>! = nil
init(_ owner:OwnerType, _ property:ReferenceWritableKeyPath<OwnerType,ValueType>)
{ (self.owner,self.property) = (owner,property) }
init(_ owner:OwnerType, get valueRef:KeyPath<OwnerType,ValueType>)
{ (self.owner,self.valueRef) = (owner,valueRef) }
override var value:ValueType!
{
get { return valueRef != nil ? owner?[keyPath:valueRef] : owner?[keyPath:property] }
set { owner?[keyPath:property] = newValue }
}
}
使用此泛型类,您可以创建对对象实例的有价值类型属性的引用,并在代码中的任何位置操作它们,就像值类型属性是引用类型一样。
// Example class with a read/write and a read-only property:
class MyObject
{
var myArray = [1,2,3,4]
var total:Int { return myArray.reduce(0,+) }
}
var instance:MyObject! = MyObject()
// create a reference to the array (valued type)
// that can be used anywhere and passed around as a parameter
let arrayRef = Reference(instance, \.myArray)
// the value is accessed and manipulated using the
// "value" property of the reference
arrayRef.value.remove(at:2)
arrayRef.value.append(5)
print(instance.myArray) // [1,2,4,5]
// Read-only properties can also be manipulated as
// references
let valueRef = Reference(instance, get:\.total)
print(valueRef.value) // 12
Reference类允许传递值作为函数参数的引用
// a function that expects a reference to an array
// would be declared as follows
func changeArray(_ array:ReferenceTo<[Int]>)
{ array.value.insert(9, at: 1) }
// the reference can also be used as an inout parameter
func shift(_ array:inout [Int])
{ array = Array(array.dropFirst()) + Array(array.prefix(1)) }
changeArray(arrayRef)
shift(&arrayRef.value!)
print(instance.myArray) // [9,2,4,5,1]
...
// the reference uses a weak link to the owner
// of the referenced property or value
// so there will be no strong reference cycle issues even
// if the reference is used in an object held strongly
// by the owner itself
instance = nil
print(arrayRef.value) // none ... no more value after the owner is gone