Swift-理解值类型
在这里,我们要讲讲值类型和写时复制。在 swift 的标准库中,所有的集合类型都使用了写时复制。我们在本篇文章中看一下写时复制如何工作的,并且如何实现它。
引用类型
使用 swift 的 Data 和 NSMutableData 作对比
var sampleBytes: [UInt8] = [0x0b, 0xad, 0xf0, 0x0d]
let nsData = NSMutableData(bytes: sampleBytes, length: sampleBytes.count)
在这里,我们使用了 let 来修饰 nsData 变量,但是因为 NSMutableData 是一个引用类型,swift 的 let/var 关键字不能控制它。对于引用类型,let 只能保证 nsData 不被指向其他实例,但是我们可以修改他:
nsData.append(sampleBytes, length: sampleBytes.count)
因为我们操作的是一个对象,在以下例子中,两个对象都会发生改变:
// 两者都指向同一个对象,所以都改变了
let nsOtherData = nsData
nsData.append(sampleBytes, length: sampleBytes.count)
如果不希望 nsOtherData 也随之改变,可以使用 mutableCopy
// 这样 nsOtherData 就不会改变
let nsOtherData = nsData.mutableCopy() as! NSMutableData
nsData.append(sampleBytes, length: sampleBytes.count)
值类型
现在我们看一下 Data 类型,这是一个结构体
let data = Data(bytes: sampleBytes, count: sampleBytes.count)
这里我们使用了 let 修饰,这样就不能修改 data 的值,除非将他设置为 var。并且将 data 赋值给别的变量,修改变量也不会影响 data。
值类型和引用类型之间的差异在于,当你将一个值类型赋值给别的变量或者作为函数参数时,只是对值进行了赋值。但是将引用类型分配给其他变量时,只会创建指向内存中同一个对象的第二个引用。
当我们创建一个副本时,结构体被逐个复制。但是这不意味着 Data 的值直接被复制过去,因为 Data 有一个内部的内存引用。当结构体被复制时,只是引用被复制给新值。只有当复制的变量值改变时,才会将值复制过去。
实现写时复制
在这里我们将实现一个很简单的结构体版本,来更好的理解写时复制
struct MyData {
var data = NSMutableData()
mutating func append(_ bytes: [UInt8]) {
data.append(bytes, length: bytes.count)
}
}
接下来看看结果如何
var data = MyData()
var copy = data
data.append(sampleBytes)
Copy 还是被改变了,这是因为在结构体复制时,将引用复制了过去,这个引用指向了实际的值,所以 copy 还是被改变,以下代码就可以修复这个问题
struct MyData {
var data = NSMutableData()
mutating func append(_ bytes: [UInt8]) {
print("making a copy")
data = data.mutableCopy() as! NSMutableData
data.append(bytes, length: bytes.count)
}
}
现在 copy 的值不会被改变了,接下来重构一下结构体,更加优雅点:
struct MyData {
var data = NSMutableData()
var dataForWriting: NSMutableData {
mutating get {
print("making a copy")
data = data.mutableCopy() as! NSMutableData
return data
}
}
mutating func append(_ bytes: [UInt8]) {
dataForWriting.append(bytes, length: bytes.count)
}
}
让写时复制更高效
目前的做法是很幼稚的,我们会在每次 append 的时候都去复制,而不去管我们是这个对象的唯一拥有者,例如以下代码:
for _ in 0..<10 {
data.append(sampleBytes)
}
// This prints:
// making a copy
// making a copy
// making a copy
// making a copy
// making a copy
// making a copy
// making a copy
// making a copy
// making a copy
// making a copy
以上代码在理想情况下应该只 copy 一次,想这样做的话也很简单,使用 isKnownUniquelyReferenced 方法,这个方法可以判断传入的参数是否已经有一个强引用
struct MyData {
var dataForWriting: NSMutableData {
mutating get {
if isKnownUniquelyReferenced(&data) {
return data
}
print("making a copy")
data = data.mutableCopy() as! NSMutableData
return data
}
}
}
但是上面的代码还是没有起效的,因为 isKnownUniquelyReferenced 只适用于 swift 对象,现在我们来将 NSMutableData 包装为 swift 对象:
final class Box<A> {
// 使用这个常量
let unbox: A
init(_ value: A) {
unbox = value
}
}
struct MyData {
var data = Box(NSMutableData())
var dataForWriting: NSMutableData {
mutating get {
if isKnownUniquelyReferenced(&data) {
return data.unbox
}
print("making a copy")
data = Box(data.unbox.mutableCopy() as! NSMutableData)
return data.unbox
}
}
mutating func append(_ bytes: [UInt8]) {
dataForWriting.append(bytes, length: bytes.count)
}
}
现在我们再去进行之前的遍历操作会发现只复制了一次,有点类似 lazy
写时复制也不是时时起效
(0..<10).reduce(data) { result, _ in
var copy = result
copy.append(sampleBytes)
return copy
}
上面的写法会 copy10次,所以写时复制也不是万能的,但是一般情况下不会出现这种问题。
上面 reduce 内部做了什么?
- (0..<10) 代表了闭包会进行10次
- 接受了一个初始值参数,并将这个初始值作为第一次遍历的 result 的值
- 返回的 copy 作为下一次循环的 result 值
参考自 swift talk
作者:没阳光的午后
链接:http://www.jianshu.com/p/2adbcc8b6389
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Swift-理解值类型的更多相关文章
- C#基础知识1-深入理解值类型和引用类型
C#值类型和引用类型这个概念在刚学习的时候应该就知道了.但是我们并没有深入的去理解它.越是基础知识其实才是最有用的.对代码的优化,代码质量的提升都有帮助.通过整理本文章,对很多知识也起到了巩固的作用吧 ...
- Swift - 37 - 值类型和引用类型的简单理解
//: Playground - noun: a place where people can play import UIKit // 值类型:指的是当一个变量赋值给另外一个变量的时候, 是copy ...
- swift的值类型和引用类型
前言 最近在学设计模式中,发现 Swift 中的 struct,class 以及 enum 在一般的使用中能够做到互相替换,因此探究其背后的逻辑就十分有必要.而这一问题又引出了 Swift 中的值类型 ...
- c#基础系列1---深入理解值类型和引用类型
"大菜":源于自己刚踏入猿途混沌拾起,自我感觉不是一般的菜,因而得名"大菜",于自身共勉. 不知不觉已经踏入坑已10余年之多,对于c#多多少少有一点自己的认识, ...
- 理解C#值类型和引用类型
网上偶尔浏览到这一篇文章,还不错就修改了下分享给大家. 工作许久了,可是对C#值类型和C#引用类型却一直无法很好的理解.这两天花了不少时间查找资料,看文章,终于有所收获,在此将自己理解整理出来,方便日 ...
- 从CLR角度来看值类型与引用类型
前言 本文中大部分示例代码来自于<CLR via C# Edition3>,并在此之上加以总结和简化,文中只是重点介绍几个比较有共性的问题,对一些细节不会做过深入的讲解. 前几天一直忙着翻 ...
- NET中的引用类型和值类型 zt
.NET中的类型分为值类型和引用类型,他们在内存布局,分配,相等性,赋值,存储以及一些其他的特性上有很多不同,这些不同将会直接影响到我们应用程序 的效率.本文视图对.NET 基础类型中的值类型和引用类 ...
- js值类型与引用类型
JavaScript值类型和引用类型有哪些 (1)值类型:数值.布尔值.null.undefined. (2)引用类型:对象.数组.函数. 三.如何理解值类型和引用类型及举例 我们可以用“连锁店”和“ ...
- c# 值类型和引用类型 笔记
参考以下博文,我这里只是笔记一下,原文会更加详细 c#基础系列1---深入理解值类型和引用类型 堆栈和托管堆c# 值类型和引用类型 红色表示——“这啥?”(真实1个问题引出3个问题) CLR支持的两种 ...
- Swift 值类型和引用类型的内存管理
1.内存分配 1.1 值类型的内存分配 在 Swift 中定长的值类型都是保存在栈上的,操作时不会涉及堆上的内存.变长的值类型(字符串.集合类型是可变长度的值类型)会分配堆内存. 这相当于一个 &qu ...
随机推荐
- SQLServer 里的三种条件判断的用法:Where GroupBy Having
HAVING 子句对 GROUP BY 子句设置条件的方式与 WHERE 子句和 SELECT 语句交互的方式类似.WHERE 子句搜索条件在进行分组操作之前应用:而 HAVING 搜索条件在进行分组 ...
- 复习HTML+CSS(9)
n CSS简介 CSS:Cascading Style Sheets 层叠样式表 CSS的主要目的:是给HTML标记添加各种各样的表现(格式.样式).比如:文字样式.背景.文本样式.链接样式. 提示 ...
- P2241 统计方形(数据加强版)
题目背景 1997年普及组第一题 题目描述 有一个n*m方格的棋盘,求其方格包含多少正方形.长方形 输入输出格式 输入格式: n,m因为原来数据太弱,现规定m小于等于5000,n小于等于5000(原来 ...
- ASP.NET访问网络驱动器(映射磁盘)
也许很多朋友在做WEB项目的时候都会碰到这样一个需求: 当用户上传文件时,需要将上传的文件保存到另外一台专门的文件服务器. 要实现这样一个功能,有两种解决方案: 方案一.在文件服务器上新建一站点,用来 ...
- dubbo之多协议
(1) 不同服务不同协议 比如:不同服务在性能上适用不同协议进行传输,比如大数据用短连接协议,小数据大并发用长连接协议 consumer.xml <?xml version="1.0& ...
- 时序分析:ARIMA模型(非平稳时间序列)
转载于一篇硕士论文.... ARIMA模型意为求和自回归滑动平均模型(IntergratedAut少regressive MovingAverageModel),简记为ARIMA(p,d,q),p,q ...
- .NET 人工智能相关资料整理
机器学习组件:https://www.cnblogs.com/asxinyu/p/dotnet_Opensource_project_AccordNET.html ML.NET: ...
- TCP中的RST标志(Reset)详解
在谈RST攻击前,必须先了解TCP:如何通过三次握手建立TCP连接.四次握手怎样把全双工的连接关闭掉.滑动窗口是怎么传输数据的.TCP的flag标志位里RST在哪些情况下出现.下面我会画一些尽量简化的 ...
- eas之指定虚模式
KDTable支持三种取数模式:实模式.虚模式分页.虚模式分组,默认为实模式.// 实模式table.getDataRequestManager().setDataRequestMode(KDTDat ...
- 一些诗词摘抄qwq
声明: 有些违规内容就删掉了--大家都能理解吧qwq 雾失楼台,月迷津渡,桃源望断无寻处.可堪孤馆闭春寒,杜鹃声里斜阳暮.--秦观<踏莎行·郴州旅舍> 郴江幸自绕郴山,为谁流下潇湘去?-- ...