【Alamofire】【Swift】属性包装器注解@propertyWrapper

news2024/12/28 3:46:54

Alamofire 中的源码例子

import Foundation

private protocol Lock {
    func lock()
    func unlock()
}

extension Lock {
    /// Executes a closure returning a value while acquiring the lock.
    ///
    /// - Parameter closure: The closure to run.
    ///
    /// - Returns:           The value the closure generated.
    func around<T>(_ closure: () throws -> T) rethrows -> T {
        lock(); defer { unlock() }
        return try closure()
    }

    /// Execute a closure while acquiring the lock.
    ///
    /// - Parameter closure: The closure to run.
    func around(_ closure: () throws -> Void) rethrows {
        lock(); defer { unlock() }
        try closure()
    }
}

#if os(Linux) || os(Windows)

extension NSLock: Lock {}

#endif

#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
/// An `os_unfair_lock` wrapper.
final class UnfairLock: Lock {
    private let unfairLock: os_unfair_lock_t

    init() {
        unfairLock = .allocate(capacity: 1)
        unfairLock.initialize(to: os_unfair_lock())
    }

    deinit {
        unfairLock.deinitialize(count: 1)
        unfairLock.deallocate()
    }

    fileprivate func lock() {
        os_unfair_lock_lock(unfairLock)
    }

    fileprivate func unlock() {
        os_unfair_lock_unlock(unfairLock)
    }
}
#endif

/// A thread-safe wrapper around a value.
@propertyWrapper
@dynamicMemberLookup
final class Protected<T> {
    #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
    private let lock = UnfairLock()
    #elseif os(Linux) || os(Windows)
    private let lock = NSLock()
    #endif
    private var value: T

    init(_ value: T) {
        self.value = value
    }

    /// The contained value. Unsafe for anything more than direct read or write.
    var wrappedValue: T {
        get { lock.around { value } }
        set { lock.around { value = newValue } }
    }

    var projectedValue: Protected<T> { self }

    init(wrappedValue: T) {
        value = wrappedValue
    }

    /// Synchronously read or transform the contained value.
    ///
    /// - Parameter closure: The closure to execute.
    ///
    /// - Returns:           The return value of the closure passed.
    func read<U>(_ closure: (T) throws -> U) rethrows -> U {
        try lock.around { try closure(self.value) }
    }

    /// Synchronously modify the protected value.
    ///
    /// - Parameter closure: The closure to execute.
    ///
    /// - Returns:           The modified value.
    @discardableResult
    func write<U>(_ closure: (inout T) throws -> U) rethrows -> U {
        try lock.around { try closure(&self.value) }
    }

    subscript<Property>(dynamicMember keyPath: WritableKeyPath<T, Property>) -> Property {
        get { lock.around { value[keyPath: keyPath] } }
        set { lock.around { value[keyPath: keyPath] = newValue } }
    }

    subscript<Property>(dynamicMember keyPath: KeyPath<T, Property>) -> Property {
        lock.around { value[keyPath: keyPath] }
    }
}

extension Protected where T == Request.MutableState {
    /// Attempts to transition to the passed `State`.
    ///
    /// - Parameter state: The `State` to attempt transition to.
    ///
    /// - Returns:         Whether the transition occurred.
    func attemptToTransitionTo(_ state: Request.State) -> Bool {
        lock.around {
            guard value.state.canTransitionTo(state) else { return false }

            value.state = state

            return true
        }
    }

    /// Perform a closure while locked with the provided `Request.State`.
    ///
    /// - Parameter perform: The closure to perform while locked.
    func withState(perform: (Request.State) -> Void) {
        lock.around { perform(value.state) }
    }
}

/// A thread-safe wrapper around a value.
@propertyWrapper
@dynamicMemberLookup
final class Protected {}

使用:
请添加图片描述

参考:链接

属性包装器(Property Wrappers)

A property wrapper adds a layer of separation between code that manages how a property is stored and the code that defines a property.

属性包装器,用来修饰属性,它可以抽取关于属性重复的逻辑来达到简化代码的目的

For example, if you have properties that provide thread-safety checks or store their underlying data in a database, you have to write that code on every property. When you use a property wrapper, you write the management code once when you define the wrapper, and then reuse that management code by applying it to multiple properties.

比如:如果你有属性提供了线程安全检查或将数据存到数据库功能,那么你将需要为每个属性编写类似代码。有了属性包装器,我们就可以避免类似重复代码

如何使用属性包装器

To define a property wrapper, you make a structure, enumeration, or classthat defines a wrappedValue property.

我们通过 @propertyWrapper 来标识structure, enumeration, or class来实现属性包装,有两个要求

  • 必须使用属性@propertyWrapper进行定义。
  • 它必须具有wrappedValue属性。

简单使用

使用@propertyWrapper创建一个 TwelveOrLess结构体,该结构体保证被封装的值number小于等于12,如果我们存储的值大于12,那么属性返回12

@propertyWrapper
struct TwelveOrLess {
    private var number: Int
    
    init() {
        self.number = 0
    }
    
    var wrappedValue: Int {
        get {
            number
        }
        set {
            number = min(newValue, 12)
        }
    }
}

SmallRectangle 结构体上使用TwelveOrLess,使用包装属性通过@开始拼接包装类TwelveOrLess,放置在属性之前即可。如:@TwelveOrLess

struct SmallRectangle {
    @TwelveOrLess var height: Int 
    @TwelveOrLess var width: Int
}

var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"

rectangle.height = 10
print(rectangle.height)
// Prints "10"

rectangle.height = 24
print(rectangle.height)
// Prints "12"

通过上面的代码可以看出:

  • width和height初始值为0
  • 设置height为10,其值有效,满足条件,number将会被设置为10,所以打印为10
  • 设置height为24,超过最大值12,所以number将会被设置为12,打印为12

使用 @TwelveOrLess 修饰的属性可以自动将值限制在 12 及以下。那么,当使用属性包装时,实际发生了什么呢?在通过对属性包装时编译器会自动转为下面的代码:

struct SmallRectangle {
    private var _height = TwelveOrLess()
    private var _width = TwelveOrLess()
    var height: Int {
        get { return _height.wrappedValue }
        set { _height.wrappedValue = newValue }
    }
    var width: Int {
        get { return _width.wrappedValue }
        set { _width.wrappedValue = newValue }
    }
}

也就是rectangle.height = 24 这句代码的调用路径:

  • 调用 SmallRectangle heightset 函数
  • 调用 TwelveOrLess wrappedValueset函数
  • 调用 number = min(newValue, 12) 来保证新设置的值小于等于 12

注意:当没有给 @TwelveOrLess 修饰的变量赋初始值时,默认使用 init() 初始化。

struct ZeroRectangle {
    @TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}

var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
// Prints "0 0"

设置初始值(Setting Initial Values for Wrapped Properties)

将上面的 SmallRectangle 改写为下面的代码你会发现报错 :

img

截屏2021-12-28 上午11.28.07.png

这是因为我们的 TwelveOrLess 并没有提供有参的初始化函数。只需要在 TwelveOrLess 添加初始化函数即可解决:

@propertyWrapper
struct SmallNumber {
    private var maximum: Int // 最大值
    private var number: Int

    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, maximum) }
    }

    init() {
        maximum = 12
        number = 0
    }

    init(wrappedValue: Int) {
        maximum = 12
        number = min(wrappedValue, maximum)
    }

    init(wrappedValue: Int, maximum: Int) {
        self.maximum = maximum
        number = min(wrappedValue, maximum)
    }
}

为属性直接设置值,将使用init(wrappedValue:)初始化

struct UnitRectangle {
    @SmallNumber var height: Int = 1
    @SmallNumber var width: Int = 1
}

var unitRectangle = UnitRectangle()
print(unitRectangle.height, unitRectangle.width)
// Prints "1 1"

属性包装器拥有参数,将使用init(wrappedValue:maximum:)进行初始化

struct NarrowRectangle {
    @SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
    @SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
}

var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)
// Prints "2 3"

narrowRectangle.height = 100
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
// Prints "5 4"

直接赋值和属性包装器初始化组合使用

struct MixedRectangle {
    @SmallNumber var height: Int = 1
    @SmallNumber(maximum: 9) var width: Int = 2
}

var mixedRectangle = MixedRectangle()
print(mixedRectangle.height)
// Prints "1"

mixedRectangle.height = 20
print(mixedRectangle.height)
// Prints "12"

直接赋值操作作为wrappedValue值,所以设置height为1,即相当于调用SmallNumber(wrappedValue: 1),对于width而言,相当于调用SmallNumber(wrappedValue: 2, maximum: 9)

Projecting a Value From a Property Wrapper

除了wrappedValue值,属性包装器还能通过projectedValue 用来获取你定义逻辑的一些额外状态值。比如在上面的例子中,你想获取你设置的值是否超过了限定的最大值,这个就可以用 projectedValue 来获取。

@propertyWrapper
struct SmallNumber {
    private var number: Int
    private(set) var projectedValue: Bool

    var wrappedValue: Int {
        get { return number }
        set {
            if newValue > 12 {
                number = 12
                projectedValue = true
            } else {
                number = newValue
                projectedValue = false
            }
        }
    }

    init() {
        self.number = 0
        self.projectedValue = false
    }
}

获取状态值:

struct SomeStructure {
    @SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()

someStructure.someNumber = 4
print(someStructure.$someNumber)
// Prints "false"

someStructure.someNumber = 55
print(someStructure.$someNumber)
// Prints "true"

通过 $+属性名的方式来获取 projectedValue。当设值为 4 的时候,没有大于 12,没有触发条件,所以 $someNumber 为 false;当设值为 55 的时候,大于 12,触发了条件,所以 $someNumber 为 true。

使用场景

字符串首字母大写

@propertyWrapper struct Capitalized {
    var wrappedValue: String {
        didSet { wrappedValue = wrappedValue.capitalized }
    }

    init(wrappedValue: String) {
        self.wrappedValue = wrappedValue.capitalized
    }
}

定义User,并使用Capitalized

struct User {
     var firstName: String
     var lastName: String
}

let user = User(firstName: "jack", lastName: "long")
print(user.firstName, user.lastName) // Jack Long

属性加锁使用

@propertyWrapper
class LockAtomic<T> {
    private var value: T
    private let lock = NSLock()

    public init(wrappedValue value: T) {
        self.value = value
    }

    public var wrappedValue: T {
        get { getValue() }
        set { setValue(newValue: newValue) }
    }

    // 加锁处理获取数据
    func getValue() -> T {
        lock.lock()
        defer { lock.unlock() }

        return value
    }

    // 设置数据加锁
    func setValue(newValue: T) {
        lock.lock()
        defer { lock.unlock() }

        value = newValue
    }
}

使用LockAtomic


var json: [String: String]?

json = ["a": "1"]
print(json) // Optional(["a": "1"])

参考

  • Property Wrappers
  • Property wrappers in Swift
  • Swift Property Wrappers

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/392705.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

9.SpringSecurity核心过滤器-SecurityContextPersistenceFilter

SpringSecurity核心过滤器-SecurityContextPersistenceFilter 一、SpringSecurity中的核心组件 在SpringSecurity中的jar分为4个&#xff0c;作用分别为 jar作用spring-security-coreSpringSecurity的核心jar包&#xff0c;认证和授权的核心代码都在这里面spring-security-co…

Promise入门

Promise入门 Promise的基本概念 男女相爱了&#xff0c;女方向男方许下一个承诺怀孕new Promise&#xff0c;这是会产生两种结果怀上(resolve)和没怀上(reject)&#xff0c;resolve对应then&#xff0c;reject对应catch&#xff0c;无论是否怀上都会执行finally。 <script&…

【论文速递】CASE 2022 - EventGraph: 将事件抽取当作语义图解析任务

【论文速递】CASE 2022 - EventGraph: 将事件抽取当作语义图解析任务 【论文原文】&#xff1a;https://aclanthology.org/2022.case-1.2.pdf 【作者信息】&#xff1a;Huiling You, David Samuel, Samia Touileb, and Lilja vrelid 论文&#xff1a;https://aclanthology.o…

sql server 对比两个查询性能 ,理解Elapsed Time、CPU Time、Wait Time

分析 SET STATISTICS TIME ONyour sqlSET STATISTICS TIME OFF由上图分析: cpu time 是查询执行时占用的 cpu 时间。如果了解系统的多任务机制&#xff0c;就会知道系统会将整个 cpu 时间分为一个一个时间片&#xff0c;平均分配给运行的线程——一个线程在 cpu 上运行一段时间…

《PyTorch深度学习实践9》——卷积神经网络-高级篇(Advanced-Convolution Neural Network)

一、1∗11*11∗1卷积 由下面两张图&#xff0c;可以看出1∗11*11∗1卷积可以显著降低计算量。 通常1∗11*11∗1卷积还有以下功能&#xff1a; 一是用于信息聚合&#xff0c;同时增加非线性&#xff0c;1∗11*11∗1卷积可以看作是对所有通道的信息进行线性加权&…

Air101|Air103|Air105|Air780E|ESP32C3|ESP32S3|Air32F103开发板:概述及PinOut

1、合宙Air101&#xff08;芯片及开发板&#xff09; 合宙Air101是一款QFN32 封装&#xff0c;4mm x 4mm 大小的mcu。通用串口波特率&#xff0c;设置波特率为921600。 ​ 管脚映射表 GPIO编号 命名 默认功能及扩展功能 0 PA0 BOOT 1 PA1 I2C_SCL/ADC0 4 PA4 I2C_S…

前端必备技术之——AJAX

简介 AJAX 全称为 Asynchronous JavaScript And XML&#xff0c;就是异步的 JS 和 XML(现在已经基本被json取代)。通过 AJAX 可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;无刷新获取数据。AJAX 不是新的编程语言&#xff0c;而是一种将现有的标准组…

揭秘关键一环!数据安全服务大盘点

数据安全服务&#xff0c;数据安全体系建设的关键一环。通过数据安全服务解决数据安全建设难题&#xff0c;得到越来越多的重视。不久前&#xff0c;《工业和信息化部等十六部门关于促进数据安全产业发展的指导意见》发布&#xff0c;明确“壮大数据安全服务”&#xff0c;推进…

VScode 插件【配置】

写这篇博客的原因&#xff1a; vscode 很久以前的插件&#xff0c;忘记是干什么的了记录 vscode 好用的插件 插件介绍&#xff08;正文开始&#xff09; Auto Rename tag 开始/关闭标签内容 同步 Chinese (Simplified) VScode 中文化 CSS Peek 通过 html 代码查找到引用的样式…

Linux - 磁盘I/O性能评估

文章目录概述RAID文件系统与裸设备的对比磁盘I/O性能评判标准常用命令“sar –d”命令组合“iostat –d”命令组合“iostat –x”单独统计某个磁盘的I/O“vmstat –d”命令组合小结概述 RAID 可以根据应用的不同&#xff0c;选择不同的RAID方式 如果一个应用经常有大量的读操…

Flink(Java版)学习

一、Flink流处理简介 1.Flink 是什么 2.为什么要用 Flink 3.流处理的发展和演变 4.Flink 的主要特点 5.Flink vs Spark Streaming 二、快速上手 1.搭建maven工程 2.批处理WordCount 3.流处理WordCount 三、Flink部署 1.Standalone 模式 2.Yarn 模式 3.Kubernetes 部署 四、F…

PyTorch深度学习:60分钟入门

PyTorch深度学习&#xff1a;60分钟入门 本教程的目的: 更高层级地理解PyTorch的Tensor库以及神经网络。训练一个小的神经网络来对图像进行分类。 本教程以您拥有一定的numpy基础的前提下展开 Note: 务必确认您已经安装了 torch 和 torchvision 两个包。 这是一个基于Pytho…

Cannot resolve symbol ‘String‘或Cannot resolve symbol ‘System‘ ——IDEA

IDEA中运行报错&#xff0c;“Cannot resolve symbol ‘String‘”解决方案 ‘System‘解决 参考一&#xff1a;(31条消息) IDEA2021 提示“Cannot resolve symbol ‘String‘”解决方案_idea无法解析符号string_YT20233的博客-CSDN博客https://blog.csdn.net/CNMBZY/article…

docker 的安装与卸载

一 卸载旧版本的 Docker 1.列出系统中已安装的docker包 yum list installed | grep docker 示例 2.卸载docker yum -y remove docker-ce-cli.x86_64 yum -y remove docker-ce.x86_64 yum -y remove containerd.io 示例 二 Docker的安装 1.安装 Docker 所需的依赖&#…

虹科方案| 助力高性能视频存储解决方案-1

虹科电子科技有限公司是ATTO技术公司在中国的官方合作伙伴。依附于我们十多年的客户积累和方案提供经验&#xff0c;虹科与 ATTO共同致力于为数据密集型计算环境提供网络和存储连接以及基础架构解决方案&#xff0c;为客户提供更高性能的产品与服务。无论您的工作流程面临何种挑…

分页插件——PageHelper

文章目录1 分页查询——分页插件&#xff08;PageHelper&#xff09;1.1 概述1.2 代码实现1.2.1 在pom.xml引入依赖1.2.2 Mapper数据访问层实现1.2.3 Service业务逻辑层实现1.2.4 postman测试试2 带条件的分页查询——分页插件&#xff08;PageHelper&#xff09;2.1 需求2.2 代…

ubuntu 20.04安装ROS体验小海龟转圈圈

文章目录前言一、ros安装1、添加ROS软件源&#xff1a;2、添加密钥&#xff1a;3、安装ROS&#xff1a;4、初始化rosdep:5、设置环境变量&#xff1a;6、安装rosinstall二、体验小海龟案例1.键盘控制小海龟&#xff1a;1、新建一个终端运行ros2、新建终端启动小海龟的仿真器3、…

网络资源面经3

文章目录hive 与 mysql 的区别类加载器的种类&#xff0c;有什么机制&#xff0c;机制有何用处MapReduce实现wordcount流程full GC 和 old GC 区别避免频繁的Full GChive 与 mysql 的区别 数据存储位置 hive数据存储在hdfs上&#xff0c;mysql的数据存储在本地磁盘中。数据规模…

自排查Nginx域名失效

静态分离 用户访问域名——>Nginx——>网关——>微服务 问题&#xff1a; 访问域名后发现不能跳转到首页 检查&#xff1a; 1.检测hosts文件 结果&#xff1a;正确配置域名没有发现问题 2.检查Nginx配置&#xff08;Nginx总配置、服务配置&#xff09; Nginx总配置 服…

【需求响应】基于数据驱动的需求响应优化及预测研究(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…