0

I'm Trying to initialise a 2D array with objects in every cell. I started with a class,

class cell {
    var up = 1
    var down = 1
    var right = 1
    var left = 1
}

And then Initialise my Array as such:

let row = Array<cell!>(count: 10, repeatedValue: cell())
let myArray = Array(count: 10, repeatedValue: row)

Now that works fine... Until I change the property of one of the objects.

myArray[0][0].left = 0

Then ALL objects in the Array have their "left" property set to 0.

How can I create objects that are independent of each other in the Array? (without using for-loops to append each item individually)

3
  • How do you add object into array? Do you create new cell instance every time before you add it to the array? Commented Feb 15, 2016 at 14:02
  • For this example, make cell a struct instead of a class. Commented Feb 15, 2016 at 14:07
  • 1
    "Almost" duplicate of stackoverflow.com/questions/32921425/…. Commented Feb 15, 2016 at 14:16

4 Answers 4

3

This won't work with Cell being a class because that is a reference type, so every cell in your array will be a reference to the same cell. If you can, change Cell to be a struct instead of a class. As a value type, every cell will be a unique copy.

If Cell must be a class, you could create myArray using nested maps, (which technically is still loops):

let myArray:[[Cell!]] = (1...10).map { _ in (1...10).map { _ in Cell() } }

Note, you should use names starting with an uppercase letter for class and struct names, which is why I change your cell to Cell.

Sign up to request clarification or add additional context in comments.

2 Comments

Very "Swifty" answer! (voted.) I need to get better at thinking in functional programming terms.
Very nice use of map
3

It's because Cell is a class and classes are reference types. So the first line creates a row with 10 references to the same cell. The second line creates 10 unique rows (arrays are structs therefore value types) but all the unique rows have 10 references to the same cell.

There are several ways to resolve this. You can make sure you create 100 unique cells:

var array = [[Cell]]()
for _ in 0 ..< 10
{
    var row = [Cell]()
    for _ in 0 ..< 10
    {
        row.append(Cell())
    }
    array.append(row)
}

Or, you might consider making Cell a struct which is probably the best idea if a Cell is as simple as your example and you don't need inheritance.

struct Cell
{
    var up = 1
    var down = 1
    var right = 1
    var left = 1
}

In which case you can initialise the array the way you expect.

var array = [[Cell]](count: 10, repeatedValue: [Cell](count: 10, repeatedValue: Cell())) 

1 Comment

Jeremy's answer is better than mine since he unrolled both the inner and outer initialization and solved the problem completely, whereas I just hinted at the problem with the inner object.
0

Your class cell works is a reference type. What does that mean ? Well, Apple's explanation about the topic is quite sufficient :

Types in Swift fall into one of two categories: first, “value types”, where each instance keeps a unique copy of its data, usually defined as a struct, enum, or tuple. The second, “reference types”, where instances share a single copy of the data, and the type is usually defined as a class. In this post we explore the merits of value and reference types, and how to choose between them.

By declaring cell as a class, this is a reference type.

Refence type

// let's create an object
let cell1 = Cell()
// now we add a reference to this object
let cell2 = cell1

// now we change the top of cell1
cell1.up = 10

NSLog("\(cell2.up)")  
// -> outputs : 10
// Since cell1 and cell2 represents the same object, 
// you can't modify cell1 without modifying ce22.

What happen if you declares cell as a structinstead of a class? It becomes a value type :

Value type

// let's create an object
let cell1 = Cell()

// now we create a new object, cell2, with the value initialized 
// with cell1's value. It's a copie, not a reference
let cell2 = cell1

// now we change the top of cell1
cell1.up = 10

NSLog("\(cell2.up)")  
// -> outputs : 0
// Here cell1 and cell2 are totally different objects

Comments

0

What you are doing won't work. If you try to create an array and populate it repeatedValue and the repeated value is an object, the array contains multiple references to the same object. (Objects are a "reference type". When added to a container what gets added is a reference to the object, not a copy.)

You need to create a new object for each entry in your outer array:

(Edited to create an array of String objects since I don't have access to the OP's cell class)

typealias rowArray = [String!]
var myArray = [rowArray]()
for i in 1...10
{
  myArray.append(rowArray(count: 10, repeatedValue: "Foo"))
}

Note that you're going to face the same problem with your inner array. You don't have an array of unique cell objects - you have an array that contains multiple references to a single cell object.

4 Comments

Your example is misleading because Int! is a value type, not a reference type.
True. I got lazy. I wanted to syntax-check the code in a playground, and did not have the OP's definition of "cell" so I changed it to Int instead, but that changes the nature of the problem. Thanks for pointing that out.
Sorry to keep badgering you, but String! is a value type as well.
Is it? I thought String was an object/reference type?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.