Swift: Creating an Array with a Default Value of distinct object instances

I noticed a bit weird (and dangerous IMHO) behavoir in Creating an Array with a Default Value. As stated in Swift 2.1: Collection Types

Swift’s Array type also provides an initializer for creating an array of a certain size with all of its values set to the same default value. You pass this initializer the number of items to be added to the new array (called count) and a default value of the appropriate type (called repeatedValue):

The point is: same default value; in order to understand how it work, I tried to create an array of elements of this example class

class User {
  private struct Shared {
    static var sequence: Int = 0
  }

  var id: Int
  var thinkTime: NSTimeInterval // typealias di Double

  init (thinkTime: NSTimeInterval) {
    User.Shared.sequence = User.Shared.sequence+1
    id = User.Shared.sequence
    self.thinkTime = thinkTime
  }
}

and this testing code:

let  howManyUsers: Int = 3
var users = [User](count: howManyUsers, repeatedValue:User(thinkTime: 10.0))
let u2: User = User(thinkTime: 10)
let u3: User = User(thinkTime: 10)
users.append(u2)
users.append(u3)
users[1].thinkTime = 20
users[3].thinkTime = 30

for u in users {
  print("User id:\(u.id) thinktime:\(u.thinkTime)")
}

gives:

User id:1 thinktime:20.0     
User id:1 thinktime:20.0
User id:1 thinktime:20.0
User id:2 thinktime:30.0
User id:3 thinktime:10.0

that definitively proof the initializer with the number of items to be added to the new array and a default value of the appropriate type are: the same object instance

Which is the way, as concise and smart as possible, to obtain a array of distinct object instances , instatiated with the same default value ( not the same instance but a number of instances initialized with the same default value ) ?


Classes are reference types, therefore – as you noticed – all array elements in

var users = [User](count: howManyUsers, repeatedValue:User(thinkTime: 10.0))

reference the same object instance (which is created first and then passed as an argument to the array initializer).

For a struct type you would get a different result.

A possible solution:

var users = (0 ..< howManyUsers).map { _ in User(thinkTime: 10.0) }

Here, a User instance is created for each of the array indices.

If you need that frequently then you could define an array init method which takes an "autoclosure" parameter:

extension Array {
    public init(count: Int, @autoclosure elementCreator: () -> Element) {
        self = (0 ..< count).map { _ in elementCreator() }
    }
}

var users = Array(count: howManyUsers, elementCreator: User(thinkTime: 10.0) )

Now the second argument User(thinkTime: 10.0) is wrapped by the compiler into a closure, and the closure is executed for each array index.


Update for Swift 3:

extension Array {
    public init(count: Int, elementCreator: @autoclosure () -> Element) {
        self = (0 ..< count).map { _ in elementCreator() }
    }
}