前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Swift-属性包装器,运行时反射

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

原创
作者头像
Wilbur-L
发布2022-08-07 12:32:47
7360
发布2022-08-07 12:32:47
举报
文章被收录于专栏:iOS底层原理iOS底层原理

一·单例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 等等。同时我们还需要提供一个注册方法,把它注册到你想要改变的类当中去

三·效果

效果

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一·单例Singleton
  • 二·场景
  • 三·效果
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档