简体   繁体   中英

Storing a reference to array in swift

I want to pass an array to an object and store a reference to this array. I want to be able to modify this array within this object and make sure that it's modified everywhere else.

Here is what I am trying to accomplish (how the code doesn't work)

class Foo {
   var foo : Array<Int>

   init(foo: Array<Int>) {
      self.foo = foo
   }

   func modify() {
      foo.append(5)
   }
}

var a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
print(a) // My goal is that it will print 1,2,3,4,5

My findings so far

A) The array (by default) are passed strange way. It's a reference until you modify an array length. As soon as you modify a length it will be copied and modified. As result, if I append or delete anything from it in the object it won't be seen outside

B) I can use inout on a function parameter. This will allow me to modify it within this function. However, as soon as I will try to assign it to some object member I am again struck by A)

C) I can wrap an array in some Container class. This probably is the cleanest way. However, I serialize/deserialize these objects and I would rather not put it in Container (because I will have to work around some things for serialization and deserialization and sending it to the server).

Are there anything else? Am I missing some Swift construct which allows me to do that?

You'll have to use an NSArray or NSMutableArray for this because Swift Arrays are value types so any assignment will make a copy.

You could make use of Swifts (very un-swifty) UnsafeMutablePointer .

Since (from your post) the behaviour references to arrays can't really seem be trusted, instead keep an UnsafeMutablePointer companion to the class inner array foo as well as any "external" arrays that you want to be binded to foo , in the sense that they are both just pointers to same address in memory.

class Foo {
    var foo : [Int]
    var pInner: UnsafeMutablePointer<Int>

    init(foo: [Int]) {
        pInner = UnsafeMutablePointer(foo)
        self.foo = Array(UnsafeBufferPointer(start: pInner, count: foo.count))
    }

    func modify(inout pOuter: UnsafeMutablePointer<Int>) {
        foo.append(5) // <-- foo gets new memory adress
        pInner = UnsafeMutablePointer(foo)
        pOuter = pInner
    }
}

var a = [1,2,3,4] // first alloc in memory
var pOuter: UnsafeMutablePointer<Int> = UnsafeMutablePointer(a)
var bar = Foo(foo: a) // 'bar.foo' now at same address as 'a'
print(bar.foo) // [1,2,3,4]
bar.modify(&pOuter) // -> [1,2,3,4,5]
a = Array(UnsafeBufferPointer(start: pOuter, count: bar.foo.count))

/* Same pointer adress, OK! */
print(bar.pInner)
print(pOuter)

/* Naturally same value (same address in memory) */
print(bar.foo)
print(a)

Pointers can be dangerous though (hence the fitting type name), and, again, very un-swifty. Anyway...

/* When you're done: clear pointers. Usually when using
   pointers like these you should take care to .destroy
   and .dealloc, but here your pointers are just companions
   to an Array property (which has a pointer an reference
   counter itself), and the latter will take care of the 
   objects in memory when it goes out of scope.            */
bar.pInner = nil
pOuter = nil

Now, what happens when either a or foo goes out of scope, will it break the variable that are not out of scope, or does Swift contain some clever reference counting that realises a memory address is still in use? I haven't investigated this, but feel free to indulge yourself in that.

From the Swift Programming Language ,

Structures are always copied when they are passed around in your code, and do not use reference counting.

If you examine the contents of the array variable, you will see that indeed the append works:

class Foo {
  var foo : Array

  init(_ foo: Array) {
    self.foo = foo
  }

  func modify() {
    foo.append(5)
  }

  func printFoo() {
    print("self.foo: \(foo)")
  }
}

let a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
bar.printFoo()
print("a: \(a)")

produces

self.foo: [1, 2, 3, 4, 5]
a: [1, 2, 3, 4]

You have taken a copy of a , not a reference to a .

a is declared a constant hence cannot be modified. If you are planning to modify the contents of a, declare it as a variable. ie,

var a = [1,2,3,4]

I haven't tested this but, as you are using a class to wrap the array, I see no reason why the following would not work.

class Foo {
   var foo : Array<Int>

   init(foo: inout Array<Int>) {
      self.foo = foo
   }

   func modify() {
      foo.append(5)
   }
}

let a = [1,2,3,4]
let bar = Foo(&a)
bar.modify()
print("a: \(a)") // a: [1,2,3,4,5]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM