簡體   English   中英

更改在變量的 willSet 塊中設置的值

[英]Change the value that is being set in variable's willSet block

我試圖在設置之前對正在設置的數組進行排序,但willSet的參數是不可變的,並且sort會改變值。 我怎樣才能克服這個限制?

var files:[File]! = [File]() {
    willSet(newFiles) {
        newFiles.sort { (a:File, b:File) -> Bool in
            return a.created_at > b.created_at
        }
    }
}

為了把這個問題從我自己的項目上下文中提出來,我提出了這個要點:

class Person {
    var name:String!
    var age:Int!

    init(name:String, age:Int) {
        self.name = name
        self.age = age
    }
}

let scott = Person(name: "Scott", age: 28)
let will = Person(name: "Will", age: 27)
let john = Person(name: "John", age: 32)
let noah = Person(name: "Noah", age: 15)

var sample = [scott,will,john,noah]



var people:[Person] = [Person]() {
    willSet(newPeople) {
        newPeople.sort({ (a:Person, b:Person) -> Bool in
            return a.age > b.age
        })

    }
}

people = sample

people[0]

我收到錯誤消息,指出newPeople不是可變的,而sort正試圖改變它。

不可能改變willSet的值。 如果您實現willSet觀察者,它將新的屬性值作為常量參數傳遞。


修改它以使用didSet怎么didSet

 var people:[Person] = [Person]() { didSet { people.sort({ (a:Person, b:Person) -> Bool in return a.age > b.age }) } }

willSet在值被存儲之前被調用。
didSet在存儲新值后立即調用。

您可以在此處閱讀有關財產觀察者的更多信息https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html

您還可以編寫如下所示的自定義 getter 和 setter。 但是didSet似乎更方便。

 var _people = [Person]() var people: [Person] { get { return _people } set(newPeople) { _people = newPeople.sorted({ (a:Person, b:Person) -> Bool in return a.age > b.age }) } }

在將值類型(包括數組)設置在willSet內部之前,無法更改它們。 您將需要改為使用計算屬性和后備存儲,如下所示:

var _people = [Person]()

var people: [Person] {
    get {
        return _people
    }
    set(newPeople) {
        _people = newPeople.sorted { $0.age > $1.age }
    }
}

對於喜歡抽象這種行為的人(尤其是那些習慣於 C# 的自定義屬性等功能的人)的另一種解決方案是使用Property Wrapper自 Swift 5.1 (Xcode 11.0) 起可用。

首先,創建一個可以對Comparable元素進行排序的新屬性包裝結構:

@propertyWrapper
public struct Sorting<V : MutableCollection & RandomAccessCollection>
    where V.Element : Comparable
{
    var value: V
    
    public init(wrappedValue: V) {
        value = wrappedValue
        value.sort()
    }
    
    public var wrappedValue: V {
        get { value }
        set {
            value = newValue
            value.sort()
        }
    }
}

然后假設您為Person實現了Comparable -一致性:

extension Person : Comparable {
    static func < (lhs: Person, rhs: Person) -> Bool {
        lhs.age < lhs.age
    }
    static func == (lhs: Person, rhs: Person) -> Bool {
        lhs.age == lhs.age
    }
}

你可以像這樣聲明你的屬性,它將在init或設置時自動排序:

struct SomeStructOrClass
{
    @Sorting var people: [Person]
}


// … (given `someStructOrClass` is an instance of `SomeStructOrClass`)

someStructOrClass.people = sample

let oldestPerson = someStructOrClass.people.last

警告:在頂級代碼中不允許屬性包裝器(截至撰寫本文時,Swift 5.7.1)——它們需要應用於結構、類或枚舉中的屬性 var。


為了更真實地遵循您的示例代碼,您還可以輕松地創建一個ReverseSorting屬性包裝器:

@propertyWrapper
public struct ReverseSorting<V : MutableCollection & RandomAccessCollection & BidirectionalCollection>
    where V.Element : Comparable
{
    // Implementation is almost the same, except you'll want to also call `value.reverse()`:
    //   value = …
    //   value.sort()
    //   value.reverse()
}

然后最年長的人將處於第一個元素:

// …
    @Sorting var people: [Person]
// …

someStructOrClass.people = sample
let oldestPerson = someStructOrClass.people[0]

更直接地說,如果您的用例需要通過sort(by:…)使用比較閉包而不是實現Comparable一致性,您可以這樣做:

@propertyWrapper
public struct SortingBy<V : MutableCollection & RandomAccessCollection>
{
    var value: V
    
    private var _areInIncreasingOrder: (V.Element, V.Element) -> Bool
    
    public init(wrappedValue: V, by areInIncreasingOrder: @escaping (V.Element, V.Element) -> Bool) {
        _areInIncreasingOrder = areInIncreasingOrder
        
        value = wrappedValue
        value.sort(by: _areInIncreasingOrder)
    }
    
    public var wrappedValue: V {
        get { value }
        set {
            value = newValue
            value.sort(by: _areInIncreasingOrder)
        }
    }
}
// …
    @SortingBy(by: { a, b in a.age > b.age }) var people: [Person] = []
// …

someStructOrClass.people = sample
let oldestPerson = someStructOrClass.people[0]

警告:按照SortingByinit當前工作方式,您需要指定一個初始值 ( [] )。 您可以使用額外的init來移除此要求(請參閱Swift 文檔),但是當您的屬性包裝器在具體類型上工作時(例如,如果您編寫了一個非通用的PersonArraySortingBy屬性包裝器),這種方法要復雜得多,而不是通用的 -協議屬性包裝器。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM