Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 225
Victor Ronin Avatar asked Dec 27 '15 23:12

Victor Ronin


2 Answers

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

like image 159
mattyb Avatar answered Oct 05 '22 09:10

mattyb


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.

like image 23
dfrib Avatar answered Oct 05 '22 09:10

dfrib