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

人走茶凉 提交于 2019-11-26 05:54:38

问题


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


回答1:


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



回答2:


Swift 5

extension MSRoom {
    static var dummyDefaultRoom: MSRoom = {
        let members = MSRoom.Member.dummyMembers(maxCount: 6)
        let ownerUser = members.first!.user
        var room = MSRoom(id: "98236482724", info: .init(name: "Ahmed's Room", description: "your default room", isPrivate: true), owner: ownerUser)
        room.dateCreated = Date(timeIntervalSince1970: 1565222400)
        room.currentMembers = members
        return room
    }()

}

let rooms = [MSRoom](repeating: MSRoom.dummyDefaultRoom, count: 10)


来源:https://stackoverflow.com/questions/32921425/swift-creating-an-array-with-a-default-value-of-distinct-object-instances

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!