Swift: Kopieren-Ändern



Es kommt oft vor, dass wir ein Objekt kopieren müssen, einige seiner Eigenschaften ändern, aber den Rest unverändert lassen müssen. Für diese Aufgabe gibt es eine Funktion copy().

Dies ist ein Auszug copy()aus der Kotlin-Dokumentation für die Methode . In unserer Swift-Muttersprache bedeutet dies ungefähr Folgendes:



struct User {
    let id: Int
    let name: String
    let age: Int
}

let steve = User(id: 1, name: "Steve", age: 21)

//  ,   `name`  `age`
let steveJobs = steve.changing { newUser in
    newUser.name = "Steve Jobs"
    newUser.age = 41
}


Sieht lecker aus, nicht wahr?



, Swift " ". .



 



, var let?



struct User {
    let id: Int
    var name: String
    var age: Int
}

let steve = User(id: 1, name: "Steve", age: 21)

...

var steveJobs = steve

steveJobs.name = "Steve Jobs"
steveJobs.age = 41


:



  • , , , - .
  • "". , willSet didSet .
  • , .


, — , :



//   ,   `name`
let steveJobs = User(
    id: steve.id, 
    name: "Steve Jobs",
    age: steve.age
)


, , . - , .



, , "" , .







  • , . 
  • Changeable -, , .
  • , .






, , . Key-Path , Key-Path Dynamic Member Lookup Swift 5.1 .



, generic-:



@dynamicMemberLookup
struct ChangeableWrapper<Wrapped> {
    private let wrapped: Wrapped
    private var changes: [PartialKeyPath<Wrapped>: Any] = [:]

    init(_ wrapped: Wrapped) {
        self.wrapped = wrapped
    }

    subscript<T>(dynamicMember keyPath: KeyPath<Wrapped, T>) -> T {
        get { 
            changes[keyPath].flatMap { $0 as? T } ?? wrapped[keyPath: keyPath] 
        }

        set {
            changes[keyPath] = newValue
        }
    }
}


, KeyPath. , , . .



changes[keyPath] as? T, T . nil, , . , flatMap(:), , changes .

@dynamicMemberLookup , , var.





Xcode . : .



Changeable



, , Changeable :



protocol Changeable {
    init(copy: ChangeableWrapper<Self>)
}

extension Changeable {
    func changing(_ change: (inout ChangeableWrapper<Self>) -> Void) -> Self {
        var copy = ChangeableWrapper<Self>(self)
        change(&copy)
        return Self(copy: copy)
    }
}


changing(:) , , .



, , Changeable:



extension User: Changeable {
    init(copy: ChangeableWrapper<Self>) {
        self.init(
            id: copy.id,
            name: copy.name,
            age: copy.age
        )
    }
}


, , — :



let steve = User(id: 1, name: "Steve", age: 21)

let steveJobs = steve.changing { newUser in
    newUser.name = "Steve Jobs"
    newUser.age = 30
}


, , …







changing(:) , , , :



struct Company {
    let name: String
    let country: String
}

struct User {
    let id: Int
    let company: Company
}

let user = User(
    id: 1, 
    company: Company(
        name: "NeXT", 
        country: "USA"
    )
)


user, company.name, :



let appleUser = user.changing { newUser in
    newUser.company = newUser.company.changing { newCompany in
        newCompany.name = "Apple"
    }
}


, .



. — ChangeableWrapper:



subscript<T: Changeable>(
    dynamicMember keyPath: KeyPath<Wrapped, T>
) -> ChangeableWrapper<T> {
    get {
        ChangeableWrapper<T>(self[dynamicMember: keyPath])
    }

    set { 
        self[dynamicMember: keyPath] = T(copy: newValue)
    }
}


, Changeable. Swift - . , .



, :



let appleUser = user.changing { newUser in
    newUser.company.name = "Apple"
}


, .







, , , , . Swift , .



. , , . Stencil- Sourcery, .



, , , Swift 5.1 .



 . . !




All Articles