Swift5 17.不透明类型Opaque Types, Memory Safety

2023-10-16 03:59

本文主要是介绍Swift5 17.不透明类型Opaque Types, Memory Safety,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • Opaque Types
    • 返回Opaque Types(待研究)
    • Opaque Types 和 Protocol Types的区别
  • Memory Safety
    • 对In-Out参数的访问冲突
    • Methods中的self获取冲突
    • 访问属性冲突

Opaque Types

返回值类型不透明的函数或方法将隐藏其返回值的类型信息。

protocol Shape {func draw() -> String
}struct Triangle: Shape {var size: Intfunc draw() -> String {var result = [String]()for length in 1...size {result.append(String(repeating: "*", count: length))}return result.joined(separator: "\n")}
}
let smallTriangle = Triangle(size: 3)
print(smallTriangle.draw())
// *
// **
// ***struct FlippedShape<T: Shape>: Shape {var shape: Tfunc draw() -> String {let lines = shape.draw().split(separator: "\n")return lines.reversed().joined(separator: "\n")}
}
let flippedTriangle = FlippedShape(shape: smallTriangle)
print(flippedTriangle.draw())
// ***
// **
// *struct JoinedShape<T: Shape, U: Shape>: Shape {var top: Tvar bottom: Ufunc draw() -> String {return top.draw() + "\n" + bottom.draw()}
}
let joinedTriangles = JoinedShape(top: smallTriangle, bottom: flippedTriangle)
print(joinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *

返回Opaque Types(待研究)

可以认为不透明类型就像是通用类型的逆向。具有不透明返回类型的函数必须仅返回单个类型的值。

struct Square: Shape {var size: Intfunc draw() -> String {let line = String(repeating: "*", count: size)let result = Array<String>(repeating: line, count: size)return result.joined(separator: "\n")}
}func makeTrapezoid() -> some Shape {let top = Triangle(size: 2)let middle = Square(size: 2)let bottom = FlippedShape(shape: top)let trapezoid = JoinedShape(top: top,bottom: JoinedShape(top: middle, bottom: bottom))return trapezoid
}
let trapezoid = makeTrapezoid()
print(trapezoid.draw())
// *
// **
// **
// **
// **
// *func flip<T: Shape>(_ shape: T) -> some Shape {return FlippedShape(shape: shape)
}
func join<T: Shape, U: Shape>(_ top: T, _ bottom: U) -> some Shape {JoinedShape(top: top, bottom: bottom)
}let opaqueJoinedTriangles = join(smallTriangle, flip(smallTriangle))
print(opaqueJoinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *func invalidFlip<T: Shape>(_ shape: T) -> some Shape {if shape is Square {return shape // Error: return types don't match}return FlippedShape(shape: shape) // Error: return types don't match
}struct FlippedShape<T: Shape>: Shape {var shape: Tfunc draw() -> String {if shape is Square {return shape.draw()}let lines = shape.draw().split(separator: "\n")return lines.reversed().joined(separator: "\n")}
}

Opaque Types 和 Protocol Types的区别

返回不透明类型看起来与使用协议类型作为函数的返回类型非常相似,但是这两种返回类型在是否保留类型标识方面有所不同。不透明类型是指一种特定的类型,尽管函数的调用者无法看到哪种类型。协议类型可以指符合协议的任何类型。一般而言,协议类型为您提供它们存储的值的基础类型的更多灵活性,而不透明类型使您可以对这些基础类型做出更强的保证。

Memory Safety

默认情况下,Swift防止代码中发生不安全行为。例如,Swift确保在使用变量之前先对其进行初始化,在释放变量后不访问内存,并检查数组索引是否存在越界错误。大多数时候不需要考虑访问内存,但要了解在何处可能发生冲突,来避免编写对内存的访问有冲突的代码。

在冲突的访问环境中要考虑内存访问的三个特征:访问是读还是写,访问的持续时间以及要访问的内存位置。

  • 至少一个是写访问权限。
  • 它们访问内存中的相同位置。
  • 它们的持续时间重叠。

读和写访问之间的区别通常很明显:写访问会更改内存中的位置,但读访问不会。内存中的位置是指所访问的内容,例如,变量,常量或属性。内存访问的持续时间是瞬时的或长期的。如果在访问开始之后但结束之前不可能运行其他代码,则访问是瞬时的。从本质上讲,两个瞬时访问不能同时发生。大多数内存访问是瞬时的。

对In-Out参数的访问冲突

函数可以对其所有In-Out参数进行长期写访问。在对所有非In-Out参数进行评估之后,将开始对In-Out参数进行写访问,并持续该函数调用的整个过程。如果有多个In-Out参数,则写入访问的开始顺序与参数出现的顺序相同。
这种长期写访问的结果是,即使作用域规则和访问控制允许这样做,您也无法访问以in-out形式传递的原始变量-对原始文件的任何访问都会产生冲突。例如:

var stepSize = 1func increment(_ number: inout Int) {number += stepSize
}increment(&stepSize)
// Error: conflicting accesses to stepSize

对stepSize的读取访问与对的number写入访问重叠,所以报错
在这里插入图片描述
解决此冲突的一种方法是显式复制stepSize:

// Make an explicit copy.
var copyOfStepSize = stepSize
increment(&copyOfStepSize)// Update the original.
stepSize = copyOfStepSize
// stepSize is now 2

长期对in-out参数进行写访问的另一个结果是,将单个变量作为同一函数的多个in-out参数的参数传递会产生冲突。例如:

func balance(_ x: inout Int, _ y: inout Int) {let sum = x + yx = sum / 2y = sum - x
}
var playerOneScore = 42
var playerTwoScore = 30
balance(&playerOneScore, &playerTwoScore)  // OK
balance(&playerOneScore, &playerOneScore)
// Error: conflicting accesses to playerOneScore

用playerOneScore和playerTwoScore作为参数调用不会产生冲突,这时有两个时间重叠的写访问,但是它们访问内存中的不同位置。相反,传递两个playerOneScore参数的值会产生冲突,因为它试图同时对内存中的同一位置执行两次写访问。

由于运算符是函数,因此他们也可以长期访问其输入输出参数。例如,如果balance(_:_:)是一个名为的运算符<^>,则写入playerOneScore <^> playerOneScore将导致与balance(&playerOneScore, &playerOneScore)相同的冲突。

Methods中的self获取冲突

struct Player {var name: Stringvar health: Intvar energy: Intstatic let maxHealth = 10mutating func restoreHealth() {health = Player.maxHealth}
}extension Player {mutating func shareHealth(with teammate: inout Player) {balance(&teammate.health, &health)}
}var oscar = Player(name: "Oscar", health: 10, energy: 10)
var maria = Player(name: "Maria", health: 5, energy: 10)
oscar.shareHealth(with: &maria)  // OK

在这里插入图片描述

oscar.shareHealth(with: &oscar)
// Error: conflicting accesses to oscar

mutation方法需要self在该方法的持续时间内进行写访问,而in-out参数需要teammate在相同的持续时间内进行写访问。在该方法中,两个self和都teammate指向内存中的同一位置-如下图所示。这两个写访问引用相同的内存,并且它们重叠,从而产生冲突。
在这里插入图片描述

访问属性冲突

诸如结构,元组和枚举之类的类型由各个组成值组成,例如结构的属性或元组的元素。因为这些是值类型,所以对值的任何部分进行更改都会对整个值进行更改,这意味着对属性之一的读或写访问要求对整个值的读或写访问。例如,对元组元素的重叠写访问会产生冲突:

var playerInformation = (health: 10, energy: 20)
balance(&playerInformation.health, &playerInformation.energy)
// Error: conflicting access to properties of playerInformation

下面的代码显示,对存储在全局变量中的结构的属性进行重叠的写访问时,会出现相同的错误。

var holly = Player(name: "Holly", health: 10, energy: 10)
balance(&holly.health, &holly.energy)  // Error

实际上,对结构属性的大多数访问都可以安全地重叠。例如,如果将holly上面示例中的变量更改为局部变量而不是全局变量,则编译器可以证明对结构的存储属性的重叠访问是安全的:

func someFunction() {var oscar = Player(name: "Oscar", health: 10, energy: 10)balance(&oscar.health, &oscar.energy)  // OK
}

保留内存安全性并非始终需要限制对结构属性的重叠访问。内存安全是理想的保证,但是独占访问是比内存安全更严格的要求-这意味着即使某些代码违反了对内存的独占访问,某些代码仍可以保留内存安全。如果编译器可以证明对内存的非独占访问仍然是安全的,则Swift允许使用此内存安全代码。具体来说,如果满足以下条件,则可以证明重叠访问结构的属性是安全的:

  • 仅访问实例的存储属性,而不访问计算的属性或类属性。
  • 结构是局部变量的值,而不是全局变量的值。
  • 该结构要么没有被任何闭包捕获,要么仅被不冒号的闭包捕获。

如果编译器无法证明访问是安全的,则它不允许访问。

这篇关于Swift5 17.不透明类型Opaque Types, Memory Safety的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/218901

相关文章

17.用300行代码手写初体验Spring V1.0版本

1.1.课程目标 1、了解看源码最有效的方式,先猜测后验证,不要一开始就去调试代码。 2、浓缩就是精华,用 300行最简洁的代码 提炼Spring的基本设计思想。 3、掌握Spring框架的基本脉络。 1.2.内容定位 1、 具有1年以上的SpringMVC使用经验。 2、 希望深入了解Spring源码的人群,对 Spring有一个整体的宏观感受。 3、 全程手写实现SpringM

SQL Server中,查询数据库中有多少个表,以及数据库其余类型数据统计查询

sqlserver查询数据库中有多少个表 sql server 数表:select count(1) from sysobjects where xtype='U'数视图:select count(1) from sysobjects where xtype='V'数存储过程select count(1) from sysobjects where xtype='P' SE

C#中,decimal类型使用

在Microsoft SQL Server中numeric类型,在C#中使用的时候,需要用decimal类型与其对应,不能使用int等类型。 SQL:numeric C#:decimal

说一说三大运营商的流量类型,看完就知道该怎么选运营商了!

说一说三大运营商的流量类型,看完就知道该怎么选运营商了?目前三大运营商的流量类型大致分为通用流量和定向流量,比如: 中国电信:通用流量+定向流量 电信推出的套餐通常由通用流量+定向流量所组成,通用流量比较多,一般都在100G以上,而且电信套餐长期套餐较多,大多无合约期,自主激活的卡也是最多的,适合没有通话需求的朋友办理。 中国移动:通用流量+定向流量 移动推出的套餐通常由通用流量+定向

微服务中RPC的强类型检查与HTTP的弱类型对比

在微服务架构中,服务间的通信是一个至关重要的环节。其中,远程过程调用(RPC)和HTTP是两种最常见的通信方式。虽然它们都能实现服务间的数据交换,但在类型检查方面,RPC的强类型检查和HTTP的弱类型之间有着显著的差异。本文将深入探讨这两种通信方式在类型检查方面的优缺点,以及它们对微服务架构的影响。 一、RPC的强类型检查 RPC的强类型检查是其核心优势之一。在RPC通信中,客户端和服务端都使

【Qt6.3 基础教程 17】 Qt布局管理详解:创建直观和响应式UI界面

文章目录 前言布局管理的基础为什么需要布局管理器? 盒布局:水平和垂直排列小部件示例:创建水平盒布局 栅格布局:在网格中对齐小部件示例:创建栅格布局 表单布局:为表单创建标签和字段示例:创建表单布局 调整空间和伸缩性示例:增加弹性空间 总结 前言 当您开始使用Qt设计用户界面(UI)时,理解布局管理是至关重要的。布局管理不仅关系到UI的外观,更直接影响用户交互的体验。本篇博

一二三应用开发平台应用开发示例(4)——视图类型介绍以及新增、修改、查看视图配置

调整上级属性类型 前面为了快速展示平台的低代码配置功能,将实体文件夹的数据模型上级属性的数据类型暂时配置为文本类型,现在我们调整下,将其数据类型调整为实体,如下图所示: 数据类型需要选择实体,并在实体选择框中选择自身“文件夹” 这时候,再点击生成代码,平台会报错,提示“实体【文件夹】未设置主参照视图”。这是因为文件夹选择的功能页面,同样是基于配置产生的,因为视图我们还没有配置,所以会报错。

【面试干货】Java中的四种引用类型:强引用、软引用、弱引用和虚引用

【面试干货】Java中的四种引用类型:强引用、软引用、弱引用和虚引用 1、强引用(Strong Reference)2、软引用(Soft Reference)3、弱引用(Weak Reference)4、虚引用(Phantom Reference)5、总结 💖The Begin💖点点关注,收藏不迷路💖 在Java中,除了我们常见的强引用(Strong Refer

用Ps将PSD切片并将切片保存为透明背景的图片

第一步:选择放大镜工具或者Ctrl++将要切片的部分放大。 第二步:选择移动工具单击要切片的部分,在右边的图层栏找到要切片的图层在文字上右键选择转换为智能对象,再右键该图层的文字选择栅格化图层。 第三步:单击选中所要切片的部分,然后Ctrl+A、Ctrl+C、Ctrl+N(背景内容选择透明)、Ctrl+V、Ctrl+S(将文件保存为PNG格式),这样就可以得到透明背景的图片了!

Flink SQL因类型错误导致MAX和MIN计算错误

背景 最近在做数据分析,用Flink SQL来做分析工具,因数据源的数据存在不太规范的数据格式,因此我需要通过SQL函数把我需要的数据值从VARCHAR类型的字段中把数据提取出来,然后再做MAX、MIN、SUM这些统计。怎料SUM算出来的结果准确无误,而MAX和MIN算出来的结果却始终不正确,最后发现原来是我用SQL函数提取VARCHAR类型的字段的数据,也是VARCHAR类型,所以导致MAX、