Swift-属性包装器,运行时反射

2022-08-07 12:32:47 浏览数 (1)

一·单例Singleton

假设有一个用户设置的环境变量,需要给其他类使用的话我们可以把它变成单例

代码语言:javascript复制
class Environment {
    static let shared =  Environment()
}

二·场景

当我们接收到一个 可以根据后端/北京时间来控制app的主题色有什么方法?

1.监听 2.代理 3.全局变量

现在我们来讨论第三种情况全局变量,这听起来有点不可思议。

1.定义环境变量里的设置类

代码语言:javascript复制
class UserSetting {
    var isBindingPhone = false
    var phoneBindString: String {
        if isBindingPhone {
            return "已绑定手机"
        }else {
            return "未绑定手机"
        }
    }
}

Swift提供了一个Combine的功能,它可以使我们组合不同的框架使用包括UIKit跟SwiftUI

代码语言:javascript复制
import Foundation
import Combine
import UIKit

现在我们需要把当前保存用户设置的环境变量添加上一个属性,它可以使其他类监听到属性的改变

现在环境变量需要有一个监听对象

代码语言:swift复制
class UserSetting: ObservableObject {
    var isBindingPhone = false
    var phoneBindString: String {
        if isBindingPhone {
            return "已绑定手机"
        }else {
            return "未绑定手机"
        }
    }
}

class Environment {
    static let updateChanged = Notification.Name("EnvironmentUpdate") 
    static let shared = Environment()
    private var sinks = [AnyCancellable]() 
    var values = [Any]() 
    private init() {} 
    
    func register<T: ObservableObject>(_ value: T) { //由于我们规定 用户设置的类型为可监听的对象属性,所以传参必须也
        value.append(value) //我们从所注册的类当中拿到改变的值,并储存在value数组里
        let sink = value.objectWillChange.sink { _ in
            DispatchQueue.mian.async{
            //由于所发送的监听有可能会改变UI 所以需要从主线程当中发送
                NotificationCenter.default.post(name: Self.updateChange, object: value)
                //发送Self.updateChange 并带上 更改的值
            }
        }
        sinks.append(sink)
    }
}

protocol GlobalUpdating { //所注册的监听类需要实现update方法来完成所需要的逻辑操作
    func updated()
}

extension GloablUpdating {
    func registerForUpdates() {
    //我们需要改变单个变量 而不是接收到监听改变所有
    let mirror = Mirror(reflacting: self) //反射当前类
    for child in mirror.children {
        if let result = child.vallue as? AnyGlobal {
            NotificationCenter.default.addObserve(forName:Environment.updateChanged, 
            object: result.anyWrappedValue, queue: .main, using: { _ in
                    self.update()
                    }
                )
            }
        }
        update()
    } 
}

@propertyWrapper struct Gloabl <ObjectiveType: ObservableObject>:AnyGlobal {
    var wrappValue: ObjectiveType
    var anyWrappedValue: Any {wrappValue}
    inti() {
        if let value = Environment.shared.values.first(where: {$0 is ObjectiveType}) as?
        ObjectiveType {
            self.wrappVlaue = value
        }else { //如果找不到属性所对应的值 抛出异常
            fatalError("MissingType in envrionment")
        }
    }
}

//最后代理协议还需要一个全局变量的修饰
protocol AnyGlobal {
    var anyWrappedValue: Any { get }
}

明确一个目标我们需要监听的变化的值是需要储存下来的,意味着当前改变的变量是有可能持有引用状态,因为储存的是任何类型包括类,所以我们需要使用到Swfit提供的一个类型消除的结合 AnyCancellable 来确保不会出现引用状态,同时我们需要拿到从combine模块发来的notification ,并且保持combie是线程保活的。value数组能储存那些我们所改变的环境变量它有可能是int string color。然后我们还需要允许当前的环境单例 Environment 创建他们所独有的环境 例如AppSetting UserSetting PaySetting 等等。同时我们还需要提供一个注册方法,把它注册到你想要改变的类当中去

三·效果

效果

0 人点赞