Swift: создание массива со значением по умолчанию для отдельных экземпляров объекта

Я заметил немного странное (и опасное поведение IMHO) в создании массива со значением по умолчанию. Как указано в Swift 2.1: Типы сбора

Тип Swifts Array также предоставляет инициализатор для создания массива определенного размера со всеми его значениями, установленными на одинаковое значение по умолчанию. Вы передаете этому инициализатору количество элементов, которые будут добавлены в новый массив (называемый count), и значение по умолчанию соответствующего типа (называемое repeatValue):

Точка: то же значение по умолчанию; чтобы понять, как это работает, я попытался создать массив элементов этого класса example

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
  }
}

и этот тестовый код:

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)")
}

дает:

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

которые окончательно подтверждают инициализатор с количеством элементов, добавляемых в новый массив, и значением по умолчанию соответствующего типа являются: тот же экземпляр объекта

Каким образом, как можно более кратким и умным, получить массив отдельных экземпляров объектов, установленный с одинаковым значением по умолчанию (не тот же экземпляр, но несколько экземпляров, инициализированных с тем же значением по умолчанию)?

Ответ 1

Классы являются ссылочными типами, поэтому, как вы заметили, все элементы массива в

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

ссылается на тот же экземпляр объекта (который создается первым, а затем передается как аргумент инициализатору массива).

Для типа struct вы получите другой результат.

Возможное решение:

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

Здесь для каждого из индексов массива создается экземпляр User.

Если вам это нужно часто, вы можете определить метод init массива, который принимает параметр "автозакрытия":

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) )

Теперь второй аргумент User(thinkTime: 10.0) компилятором в закрытие, а закрытие выполняется для каждого индекса массива.


Обновление для Swift 3:

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