从0到1学习Swift

Swift @propertyWrapper

2021-12-31  本文已影响0人  一粒咸瓜子

属性包装特性给了我们一个机会,可以在一定程度上简化语言的模板代码,并且通过“标注”的方式来改变特性。
它与自定义 getter 和 setter 做的事情相似,只不过功能更强大,且不需要到处重复去写一样的代码。

说简单点: 通过 @propertyWrapper 可以移除掉一些重复或者类似的代码。
在 Swift 中,这一特性的正式名称是属性包装 (Property Wrapper)。

使用

@propertyWrapper
struct FileStorage<T: Codable> {
    var value: T?
    let directory: FileManager.SearchPathDirectory
    let fileName: String
    
    init(directory: FileManager.SearchPathDirectory, fileName: String) {
        value = try? FileHelper.loadJSON(from: directory, fileName: fileName)
        self.directory = directory
        self.fileName = fileName
    }
    
    var wrappedValue: T? {
        set {
            value = newValue
            if let value = newValue {
                try? FileHelper.writeJSON(value, to: directory, fileName: fileName)
            } else {
                try? FileHelper.delete(from: directory, fileName: fileName)
            }
        }
        get {
            value
        }
    }
}

// 使用
@FileStorage(directory: .documentDirectory, fileName: "user.json")
var loginUser: User?
@propertyWrapper
struct UserDefaultStorage<T> {
    let key: String
    let initialValue: T
    
    init(_ initialValue: T, key: String) {
        self.key = key
        self.initialValue = initialValue
    }
    
    var wrappedValue: T {
        set {
            UserDefaults.standard.set(newValue, forKey: key)
            UserDefaults.standard.synchronize()
        }
        get {
            UserDefaults.standard.object(forKey: key) as? T ?? initialValue
        }
    }
}

// 使用
// UserDefault 无法保存枚举值,只能保存对应的 rawValue,所以在外面包一层映射
// 使用 rawValue 的条件是标明其类型:
enum Sorting: Int {
  //...
}

var sorting: Sorting {
    set { sortingRawValue = newValue.rawValue }
    get { Sorting(rawValue: sortingRawValue) ?? .id }
}
@UserDefaultStorage(Sorting.id.rawValue, key: "sorting")
var sortingRawValue: Sorting.RawValue
@UserDefaultStorage(true, key: "showEnglishName")
var showEnglishName: Bool
@UserDefaultStorage(false, key: "showFavoriteOnly")
var showFavoriteOnly: Bool

Combine 中的包装属性

在 Combine 中不论是 @State@Binding@ObjectBinding @EnvironmentObject,它们都是被 @propertyWrapper 修饰的 struct 类型。

Combine 中 State 定义的关键部分如下:

@propertyWrapper
public struct State<Value> :
DynamicViewProperty, BindingConvertible
{
    // init(initialValue:),wrappedValue 和 projectedValue 构成了一个 propertyWrapper 最重要的部分。
    public init(initialValue value: Value)
    public var value: Value { get nonmutating set }
    public var wrappedValue: Value { get nonmutating set }
    public var projectedValue: Binding<Value> { get }
}
上一篇 下一篇

猜你喜欢

热点阅读