6

Following on from this question: Is there a reason that Swift array assignment is inconsistent (neither a reference nor a deep copy)? -

I have been playing with passing objects in Swift and noticed some strange results.

To clarify the kind of behaviour i'm used to (prior to Swift) would be that of Objective C.

To give an example in one of my Applications (written in Obj C) I have the concept of a 'notification list'. - really just an array of custom objects.

In that App I often pass my global array of 'notifications' to various viewControllers which provide a UI to update the list.

When I pass the global array to a child viewController I assign it to a local array variable in the recipient object. Then, simply by updating/changing the local array these changes are reflected in the global array on the rootViewController. I understand this behaviour is implicit in Objective C as objects as passed by reference, but this is really handy and I have been trying to replicate this behaviour in Swift.

However whilst I have been rewriting my App in Swift I've hit a wall.

I first tried to pass a Swift array of strings (not NSMutableArray) from the rootViewController to a child viewController (as described above).

Here is the behaviour when passing in the array of Strings the child viewController:

I Pass in:

[Bill, Bob, Jack] and then assign this passed array to a local array for local modification,

Then I append the String “Frank” to the local array

The results are:

Local array = [Bill, Bob, Jack, Frank]

Global array = [Bill, Bob, Jack]

No changes to the local array are reflected back to the global array. - The SAME result occurs for a change of element (without changing the length of the array.)

I have also tried the above experiment with a more real world example - passing in an array of my custom 'notification' objects to a child viewController. The SAME result occurs with none of the changes to the locally assigned array of custom objects being reflected to the original global array that was passed in.

This behaviour is not desirable to me, I assume the best practice here is to use delegate protocols to pass the modified array (or whatever object) back to the parent object and then to manually update the global array?? - if so this creates quite an extra workload over the Objective C style behaviour.

Finally I did try the inout keyword, which effectively lets you directly modify the function parameter var thats passed to the destination object.

Changes are reflected back to the global array (or object) However the problem is, if the input parameter is assigned to a local variable (to edit outside of scope of the init function) changes to the local variable are still not reflected in global scope.

I hope the above makes sense - It's really stifling my productivity with Swift.

Am I missing something or is this schizophrenic behaviour expected?

If so what is best practice on passing modified data back, delegates?

Community
  • 1
  • 1
Woodstock
  • 22,184
  • 15
  • 80
  • 118
  • 1
    How can you possibly vote to close - you obviously didn't even read my question. - I referenced that question at THE START of my question. It's a totally different question. – Woodstock Jun 24 '14 at 21:08
  • 1
    You asked a good question! Ignore those morons who downvote or even close other's question without any reason. – Bagusflyer Jul 28 '14 at 02:46

2 Answers2

3

The linked question provides the answer - it is for performance.

The behaviour may not be desirable for you, but I would say that relying on side-effects from calling methods to modify parameters is the behaviour that is not considered desirable - particularly in a multi-threaded, multi-core environment where data structures can be corrupted.

A design that relies on side-effects is flawed, in my opinion.

If functions need to modify the "global" then they should either return the new value, or if that isn't possible then you should wrap your array inside an object and provide appropriate functions to manipulate the data values.

Swift blurs the lines between intrinsic and object somewhat with arrays, which makes it a little confusing - in Objective-C an NSMutableArray is an object so it always passed by reference.

For notifying other objects that the data has changed you can use an observer pattern. The typical delegate pattern only has a single registered delegate - With an observer pattern you can have multiple registered observers.

You can do this through NSNotificationCenter or an array of "delegates". The former has the advantage of decoupling the code more than delegation

Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • This seems crazy to me, it's such an overhead to pass back objects and maintain state and synchronicity of 'local' and 'global' objects. It's very limiting, especially as applications grow. You suggest wrapping the array which sounds good, until the same problem is reached with the array of wrapping objects..... In the Viewcontroller example above what would you suggest is best practice, using delegates to pass data back on every change? - why did they even bother with stuff like inout? – Woodstock Jun 24 '14 at 21:14
  • I would consider it best practice to wrap the array inside an object. In object oriented programming you only use an intrinsic type where it meets your needs - A swift array doesn't meet your needs, so create an object that does. It is also arguably better style and gives you freedom to choose a different internal data structure if you need to. You could also just use an NSMutableArray instead of a Swift array – Paulw11 Jun 24 '14 at 21:18
  • Intersting, thanks for the feedback. Do you not agree it's limiting not knowing when an object is being passed by reference and when it's not? -- Do you think a delegate protocol would also work correctly to pass back data between objects? – Woodstock Jun 24 '14 at 21:21
  • inout exists because there are some accepted use cases - such as passing an NSError - I don't think it is particularly good style, but it has been around for 40 years of C/Unix – Paulw11 Jun 24 '14 at 21:22
  • Thanks for the info, I think this kind of behaviour will potentially stop Swift from going prime time. I seem to be the only person who has issues like this so I must just be plain wrong. – Woodstock Jun 24 '14 at 21:26
  • It comes down to understanding the difference between an intrinsic and an object. Swift blurs the lines somewhat which adds to the confusion, but in Objective-C an NSMutableArray is an object so it always passed by reference. The typical delegate pattern only has a single registered delegate - You can use an observer pattern where you have multiple registered observers. You can do this through NSNotificationCenter or an array of "delegates". The former has the advantage of decoupling the code more than delegation – Paulw11 Jun 24 '14 at 21:27
  • Oh Ok - so if i'm understanding you correctly - a delegate protocol is a good way to pass data back to a single concerned object, and something like notification centre for multiple objects... and If I wrap my array in an object (or use NSMutableArray) I'll get the ObjC style? - so are all true objects (class objects) in swift passed by reference and behave like the oBj C example? – Woodstock Jun 24 '14 at 21:32
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/56232/discussion-between-paulw11-and-john-woods). – Paulw11 Jun 24 '14 at 21:32
  • In my case, I wrapped the array in an object and pass it through a property of another object. But when I was trying to modify the element of the array. I got an error **'@lvalue $T8' is not identical to 'Int'** which make no sense for me at all. – Bagusflyer Jul 28 '14 at 02:50
1

Why don't you create a Model class that contains the array as a var. Add methods to the Model class to manipulate the array and store the new instance in the property. Create a single instance of the Model class at startup and pass it to the view controllers. They all access the array through the Model or through methods in the Model class. The behavior of Swift (where it copies the array on change of size) will be hidden from all of the view controllers.

Brian Walker
  • 8,658
  • 2
  • 33
  • 35
  • not a bad suggestion thanks - it did occur to me, however it's such a lot of hassle compared to just passing by reference. – Woodstock Jun 24 '14 at 21:23
  • This actually is exactly what I did. But I got compiling errors. If I modify the value of the element of the array, I got **'@lvalue $T8' is not identical to 'Int'**. If I call the method in Model class. I got **Immutable value of type 'MyObject' only has mutating members named 'myMethod'**. I really don't understand. My method is **mutating** and my model is declared as **var** which supposed to my **mutating**. – Bagusflyer Jul 28 '14 at 02:55