Swift 5.9 新特性揭秘:非复制类型的安全与高效

这里每天分享一个 iOS 的新知识,快来关注我吧

前言

在 Swift 中,类型默认是可复制的。这种设计简化了开发过程,因为它允许值在赋值给新变量或传递给函数时轻松复制。

然而,这种便利有时会导致意想不到的问题。例如,复制单次使用的票据或重复连接数据库可能会导致无效状态或资源冲突。

为了解决这些问题,在 Swift 5.9 中引入了非复制类型。通过将类型标记为~Copyable 来实现,我们可以显式地阻止 Swift 复制它。

这保证了值的唯一所有权,并施加了更严格的约束,从而降低了出错的风险。接下来让我们详细了解一下非复制类型。

非复制类型的示例

以下是一个非复制类型的简单示例:

rust 复制代码
struct SingleUseTicket: ~Copyable {
    let ticketID: String
}

与常规值类型的行为不同,当我们将非复制类型的实例分配给新变量时,值会被移动而不是复制。如果我们尝试在稍后使用原始变量,会得到编译时错误:

bash 复制代码
let originalTicket = SingleUseTicket(ticketID: "S645")
let newTicket = originalTicket

print(originalTicket.ticketID) // 报错 'originalTicket' used after consume

需要注意的是,类不能被声明为非复制类型。所有类类型仍然是可复制的,通过保留和释放对对象的引用来实现。

非复制类型中的方法

在非复制类型中,方法可以读取、修改或消费self

借用方法

非复制类型中的方法默认是借用borrowing 的。这意味着它们只能读取实例,允许安全地检查实例而不影响其有效性。

swift 复制代码
struct SingleUseTicket: ~Copyable {
    let ticketID: String
    
    func describe() {
        print("This ticket is \(ticketID).")
    }
}

let ticket = SingleUseTicket(ticketID: "A123")

// 打印 `This ticket is A123.`
ticket.describe()

可变方法

可变方法mutating 提供了对self 的临时写访问,允许在不使实例无效的情况下进行修改。

swift 复制代码
struct SingleUseTicket: ~Copyable {
    var ticketID: String

    mutating func updateID(newID: String) {
        ticketID = newID
        print("Ticket ID updated to \(ticketID).")
    }
}

var ticket = SingleUseTicket(ticketID: "A123")

// 打印 `Ticket ID updated to B456.`
ticket.updateID(newID: "B456")

消费方法

消费方法consuming 接管self 的所有权,一旦方法完成就使实例无效。这对于完成或处置资源的任务非常有用。在调用方法后,任何尝试访问实例的操作都会导致编译错误。

scss 复制代码
struct SingleUseTicket: ~Copyable {
    let ticketID: String
    
    consuming func use() {
        print("Ticket \(ticketID) used.")
    }
}

func useTicket() {
    let ticket = SingleUseTicket(ticketID: "A123")
    ticket.use()
    
    ticket.use() // 报错 'ticket' consumed more than once
}

useTicket()

需要注意的是,我们不能消费存储在全局变量中的非复制类型,因此在我们的示例中我们将代码包装在useTicket() 函数中。

非复制类型在函数参数中的应用

当将非复制类型作为参数传递给函数时,Swift 要求我们为该函数指定所有权模型。我们可以将参数标记为借用borrowing、输入输出inout 或消费consuming,每种标记提供不同级别的访问权限,类似于类型内部的方法。

借用参数

借用所有权允许函数临时读取值,而不消耗或修改它。

swift 复制代码
func inspectTicket(_ ticket: borrowing SingleUseTicket) {
    print("Inspecting ticket \(ticket.ticketID).")
}

输入输出参数

输入输出参数inout 提供了对值的临时写访问,允许函数修改它,同时将所有权返回给调用者。

swift 复制代码
func updateTicketID(_ ticket: inout SingleUseTicket, to newID: String) {
    ticket.ticketID = newID
    print("Ticket ID updated to \(ticket.ticketID).")
}

消费参数

当一个参数被标记为消费时,函数完全接管该值的所有权,使其对于调用者无效。例如,如果我们有一个消费方法,我们可以在函数中使用它,而无需担心在函数外部使用该值。

swift 复制代码
func processTicket(_ ticket: consuming SingleUseTicket) {
    ticket.use()
}

析构函数和丢弃操作符

非复制结构体和枚举可以像类一样拥有析构函数deinit,它们会在实例生命周期结束时自动运行。

swift 复制代码
struct SingleUseTicket: ~Copyable {
    let ticketID: Int
    
    deinit {
        print("Ticket deinitialized.")
        
        // 清理逻辑
    }
}

然而,当一个消费方法和一个析构函数都执行清理时,可能会有冗余操作的风险。为了解决这个问题,Swift 引入了丢弃操作符discard

通过在消费方法中使用discard self,我们可以显式阻止调用析构函数,从而避免重复逻辑:

swift 复制代码
struct SingleUseTicket: ~Copyable {
    let ticketID: Int
    
    consuming func invalidate() {
        print("Ticket \(ticketID) invalidated.")
        
        // 清理逻辑
        
        discard self
    }
    
    deinit {
        print("Ticket deinitialized.")
        
        // 清理逻辑
    }
}

另外需要注意的是,只有当我们的类型包含可轻松销毁的存储属性时,才能使用discard。不能包含引用计数、泛型。

总结

最近几年,swift 出了很多新特性,非复制类型是其中之一,实际开发中,非复制类型很少用到,但是了解这些特性,可以让我们在开发中更加得心应手。随着 Swift 的不断发展,这些类型代表了语言在性能和正确性方面的重大进步。

但是这些越来越复杂的特性也让 swift 初学者望而却步,希望这篇文章能帮助大家了解非复制类型,在实际开发中,如果需要使用非复制类型,可以参考这篇文章。

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

相关推荐
2501_9160088917 小时前
手机 iOS 系统全解析,生态优势、开发机制与跨平台应用上架实践指南
android·ios·智能手机·小程序·uni-app·iphone·webview
I烟雨云渊T17 小时前
iOS原生与Flutter的交互编程
flutter·ios·交互
马拉萨的春天18 小时前
iOS的动态库和静态库的差异区别以及静态库的好处
macos·ios·cocoa
肖老师xy18 小时前
苹果(IOS)制作开发和发布证书
ios
马拉萨的春天19 小时前
探索Objective-C中的对象复制:深入理解copy和mutableCopy
开发语言·ios·objective-c
00后程序员张19 小时前
Fiddler使用教程,全面掌握Fiddler抓包工具的配置方法、代理设置与调试技巧(HTTPHTTPS全解析)
前端·测试工具·ios·小程序·fiddler·uni-app·webview
2501_9160088919 小时前
HTTPS 下的 DDoS 防护与抓包分析实战,从检测到快速缓解的工程化打法
网络协议·ios·小程序·https·uni-app·iphone·ddos
2501_9159184119 小时前
App 使用 HTTPS 的工程化实战,从接入到真机排查的一线指南
android·ios·小程序·https·uni-app·iphone·webview
hookserver20 小时前
企业微信ipad协议接口优势
http·ios·微信·企业微信·ipad·企微
HarderCoder1 天前
Swift 枚举完全指南——从基础语法到递归枚举的渐进式学习笔记
swift