[英]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]
警告:按照SortingBy
的init
當前工作方式,您需要指定一個初始值 ( []
)。 您可以使用額外的init
來移除此要求(請參閱Swift 文檔),但是當您的屬性包裝器在具體類型上工作時(例如,如果您編寫了一個非通用的PersonArraySortingBy
屬性包裝器),這種方法要復雜得多,而不是通用的 -協議屬性包裝器。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.