Swift中 any some的作用

news2024/11/29 12:32:42

前言

在学习Swift ui看到一个函数返回了some viewview我可以理解那some是什么?

//ContentView.swift
struct ContentView_Previews: PreviewProvider{
   static var previews: some View{
       ContentView()
   }
}

如果你仔细看一些官方文档甚至还有any关键字,也可以用于替换some。那么两则区别是什么?本文为博主笔记不具备说教性。

术语

为了弄清问题需要梳理相关术语。

  • 协议(Protocol)
    Swift中用于定义实例应具有哪些属性和方法。但是这些方法返回的数据类型应是明确的,也就是说不支持协议内嵌泛型。但提供了关联类型(associatetype)以支持类型泛化问题。
protocol Animal{
    var name:String{get}
    var kind:String{get}
    func run()
}
  • 泛型 (generic)

大多数现代语言提供一种提供泛化数据类型进而完成一个通用性强代码

假设你需要提供一个函数打印其传入参数的类型和一些修饰的字符串给用户,你利用泛型编写类似如下代码

//打印传入的参数类型,因为利用泛型所以可以适配所有类型
func printObj<T>(_ param :T) ->String{
    return "type of \(type(of: param))"
}
//传入int
print(printObj(1))
//传入double
print(printObj(1.9))
  • 泛型约束(generic constraint)

约束泛型取值范围。用于缩小模板函数的适用范围

//限制泛型入参为NSObject子类型
func printObj<T>(_ param :T)->String where T : NSObject  {
    return "type of \(type(of: param))"
}
class MyClass :NSObject{}
//compile success 传入int
print(printObj(MyClass()))
//compile error:Global function 'printObj' requires that 'Double' inherit from 'NSObject'
//print(printObj(1.9))
  • 类型擦除(type erasure)
    概念和Java类似可参阅博主其他文献:
    java 泛型擦除

  • 关联类型(associatetype)
    为了解决协议支持泛型问题Swift提供 关联类型(associatetype) 以完善其语法体系。

(例子来自参考文献)

//定义一个汽车类,由于每个汽车加入的燃料是不同的,
//所以在协议中无法明确其类型
protocol Vehicle {
    var name: String { get }
    associatedtype FuelType
    func fillGasTank(with fuel: FuelType)
}
struct Gasoline {
    let name = "gasoline"
}
struct Car: Vehicle {
    let name = "car"

    func fillGasTank(with fuel: Gasoline) {
        print("Fill \(name) with \(fuel.name)")
    }
}
struct Diesel {
    let name = "diesel"
}
struct Bus: Vehicle {
    let name = "bus"
    func fillGasTank(with fuel: Diesel) {
        print("Fill \(name) with \(fuel.name)")
    }
}
  • 自要求(self requirement)

在Swift中,Self要求是指协议中的方法、属性或者是返回类型,要求返回遵循该协议的类型本身

protocol MyPro{
    func isEqual(to other: Self) -> Bool
}
  • 静态分派(static dispatch)

我们调用一个函数需要明确其函数地址,那么这个地址的索引是在编译时确定的我们就成为静态分派。(不严谨描述)

网上有较多 JAVA,C++案例 。后文会用汇编说明Swift下的情况。

  • 动态分派(dynamic dispatch)

我们调用一个函数需要明确其函数地址,那么这个地址的索引是在运行时确定的我们就成为动态分派。(不严谨描述)

可参阅C++中利用虚表实现多态。

  • 不透明类型(opaque type)

以some修饰的协议(protocl)。 主要在协议(protocl)存在自要求(self requirement)和关联类型(associatetype)中讨论。

注意不透明类型特性:

  1. 不透明类型(opaque type)经常作为某个函数的返回值。对于调用者来说返回的对象具体的实例化类是不清楚,只知道其实现类对应的协议具体类。

  2. 对于编译器来说 声明了不透明类型(opaque type) 将在编译层面固化其具体的实现子类而不能有二义性。因为不存在二义性编译器对于函数的调用将采用静态分派(static dispatch)

我们看看一个小例子:

protocol Animal{
    func isEqual(to other: Self) -> Bool
}
class Dog:Animal{
    func isEqual(to other: Dog) -> Bool {
        return true;
    }
}
class Cat:Animal{
    func isEqual(to other: Cat) -> Bool {
        return true;
    }
}
//因为返回的协议含有自要求或关联类型所以必须要声明为some MyPro或者any MyPro方允许编译
//some MyPro称为不透明类型。因为对于调用者来说只知道返回了MyPro而不知道具体的实例子类
func myFunc(flag:Bool)->some Animal{
    
//    注意if 和else代码是不能通过编译的。因为这里可能返回Dog和Cat让编译器无法判别具体类型进而无法优化代码
//compil error:Function declares an opaque return type 'some Animal', but the return statements in its body do not have matching underlying types
//    if flag {
//        return Dog()
//    }else{
//        return Cat()
//    }
    //compil success.这里函数体只返回Dog类型不存在其他实现Animal的类返回
    return Dog();
}

//对于ret来说只知道是Animal类型但是不清楚到底实现的类是Dog还是Cat
var ret = myFunc(myflag)

我们看一个比较晦涩难懂的any 和some 影响的实际代码。

protocol Flyable {
    func fly()
    associatedtype types
}
struct Bird: Flyable {
    typealias types = Int
    
    var name:String
    init(_ name:String){
        self.name=name
    }
    func fly() {
        print("Bird is flying\(name)")
    }
}
struct Airplane: Flyable {
    typealias types = Int
    func fly() {
        print("Airplane is flying")
    }
}

func generateSomeFlyable()->some Flyable{
    //由于some Flyable未不透明类型那么告诉编译器这里返回的一定是确定的实例对象Bird。
    return Bird("bird2")
}

func takeOff() {
    //flyable2由于不透明类型那么编译器知道他一定是Bird实现的,所以调用flyable2.fly时可以肯定知道方法是Bird的
    //因为在编译期间就可以flyable2.fly()调用函数位置
    let flyable2:some Flyable = generateSomeFlyable()
    //any+protocl称为存在类型Existential type。后文讲解
    //存在类型告诉编译器这里返回的对象不能随意推测 只知道是Flyable实现类,他可能是Bird也可能是Airplane
    //所以flyable3.fly调用时只有在运行时确定fly函数是Bird还是Airplane
    let flyable3: any Flyable = Bird("bird3")
    //line 12 静态分派 在编译期间就知道具体函数地址.
    flyable2.fly()
    //动态分派 需要经过运行时查找函数地址
    flyable3.fly()
}

takeOff()

我们查看 flyable2.fly()对应函数调用汇编指令
在这里插入图片描述
最后我们看跳转到实际的Bird.fly函数
在这里插入图片描述

flyable3.fly()对应函数调用汇编指令

在这里插入图片描述

在这里插入图片描述

你会发现flyable3的函数调用经过了大量的计算和中间跳转。性能较差。因为flyable3被告知编译器他是未知的实现类需要动态计算。
flyable2的函数调用非常简介明了高效,因为some声明告诉编译器他是明确的的实现类,所以可以进行大量的编译器优化

  • 存在类型(Existential type)

用any 去修饰的protocol类型。告知编译器他实现类是未知的,不能轻易优化。

func generateSomeFlyable(flag:Bool)->any Flyable{
   //由于any Flyable告诉了编译器 这个返回类型是不确定实现类。所以你可以随意返回Flyable子类型
   if flag {
       return Bird("bird2")
   }else {
       return Airplane()
   }
}

为什么需要any和some

如果protocol存在自要求(self requirement)关联类型(associatetype) 需要开发者告知业务情况方便进行编译优化。(博主擅自猜测 不具备权威性)

综述

结论1:
在Swift中所有包含自要求(self requirement)关联类型(associatetype) 的协议必须要使用any或者some去修饰以告知编译器如何去优化,否则只能用于用作泛型约束。

protocol Flyable {
    func fly()
    associatedtype types
}
struct Bird: Flyable {
    typealias types = Int
    
    var name:String
    init(_ name:String){
        self.name=name
    }
    func fly() {
        print("Bird is flying\(name)")
    }
}
struct Airplane: Flyable {
    typealias types = Int
    func fly() {
        print("Airplane is flying")
    }
}

//compile error:Use of protocol 'Flyable' as a type must be written 'any Flyable'
var myVar:Flyable = Airplane();
//compile success
func myFunc<T:Flyable>(_ myparam:T){
}

结论2:
some 比any 更高效。some函数调用采用静态分派,而any为动态分派。

结论3:
由于any为动态分派所以可以进行类JAVA泛型操作

protocol Flyable {
    func fly()
    associatedtype types
}
struct Bird: Flyable {
    typealias types = Int
    
    var name:String
    init(_ name:String){
        self.name=name
    }
    func fly() {
        print("Bird is flying\(name)")
    }
}
struct Airplane: Flyable {
    typealias types = Int
    func fly() {
        print("Airplane is flying")
    }
}
//compile success.因为any修饰的编译器指关心是其子类即可
var myArr :[any Flyable] = [Bird("1"),Airplane()];
//compile error:Conflicting arguments to generic parameter 'τ_0_0' ('Bird' vs. 'Airplane')
//由于some需要明确子类,但是这里集合包含Bird和Airplane存在二义性。myArr2[0].fly()时无法进行静态分派,因此无法编译
var myArr2 :[some Flyable] = [Bird("1"),Airplane()];
//compile error:Function declares an opaque return type 'some Flyable', but the return statements in its body do not have matching underlying types
func generateAnyFlyable(flag:Bool)->some Flyable{
    //由于some声明存在二义性无法通过编译器
    if flag {
        return Bird("bird2")
    }else {
        return Airplane()
    }
}
//compile success
func generateSomeFlyable(flag:Bool)->any Flyable{
    //由于any Flyable告诉了编译器 这个返回类型是不确定实现类。所以你可以随意返回Flyable子类型
    if flag {
        return Bird("bird2")
    }else {
        return Airplane()
    }
}

参考

https://swiftsenpai.com/swift/understanding-some-and-any/

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

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

相关文章

GPT实战系列-智谱GLM-4的模型调用

GPT实战系列-智谱GLM-4的模型调用 GPT专栏文章&#xff1a; GPT实战系列-实战Qwen通义千问在Cuda 1224G部署方案_通义千问 ptuning-CSDN博客 GPT实战系列-ChatGLM3本地部署CUDA111080Ti显卡24G实战方案 GPT实战系列-Baichuan2本地化部署实战方案 GPT实战系列-让CodeGeeX2帮…

日本技术,马来西亚制造:NBR SELE COT无硫手指套的革命性性能

在现代工业领域&#xff0c;对于保持生产环境的洁净和高效至关重要。而一种名为NBR SELE COT的无硫手指套正是满足这一需求的理想选择。这款手指套由日本技术开发&#xff0c;采用马来西亚原材料制造&#xff0c;凭借其卓越的性能在工业行业中广受好评。 NBR SELE COT手指套具有…

云平台一键迁移(腾讯云windos服务器迁移到阿里云windos服务器)

参考文档 https://help.aliyun.com/zh/smc/use-cases/one-click-cloud-platform-migration 迁移文档 https://cloud.tencent.com/document/product/598/37140 #腾讯密钥创建 https://cloud.tencent.com/document/product/1340/51945 安装腾讯云自动化服务助手 一.导入迁移…

web前端框架设计第二课-Vue.js简介

web前端框架设计第二课-Vue.js简介 一.预习笔记 1.Vue.js概述 Vue.js是一套用于构建用户界面的渐进式框架。本质上是一个用于开发Web前端界面的库&#xff0c;其本身具有响应式编程和组件化的特点。 Vue.js的特性&#xff1a; 轻量级 数据绑定 应用指令 插件化开发 2.V…

Flink中JobManager与TaskManage的运行架构以及原理详解

Flink中JobManager与TaskManage的运行架构详解 整体架构 Flink的运行时架构中&#xff0c;最重要的就是两大组件&#xff1a;作业管理器&#xff08;JobManger&#xff09;和任务管理器&#xff08;TaskManager&#xff09;。对于一个提交执行的作业&#xff0c;JobManager是真…

云原生:重塑未来应用的基石

随着数字化时代的不断深入&#xff0c;云原生已经成为了IT领域的热门话题。它代表着一种全新的软件开发和部署范式&#xff0c;旨在充分利用云计算的优势&#xff0c;并为企业带来更大的灵活性、可靠性和效率。今天我们就来聊一聊这个热门的话题&#xff1a;云原生~ &#x1f4…

web服务架构

1 Web服务器&#xff08;如Nginx、Apache等&#xff09;和Web应用框架&#xff08;如Flask、Django等&#xff09; Web服务器&#xff08;如Nginx、Apache等&#xff09;和Web应用框架&#xff08;如Flask、Django等&#xff09;在Web应用开发和部署中扮演着不同的角色&#xf…

飞天使-k8s知识点26-kubernetes温故知新1-pod

文章目录 创建一个podpod的启动命令和参数更改pod 镜像拉取策略 pod 的三种探针pod 探针的实现方式prestop 和 prestart 创建一个pod apiVersion: v1 # 必选&#xff0c;API 的版本号 kind: Pod # 必选&#xff0c;类型 Pod metadata: # 必选&#xff0c;元数据name: nginx # …

类和对象-3

文章目录 拷贝构造函数运算符重载 拷贝构造函数 拷贝构造函数&#xff1a;只有单个形参&#xff0c;该形参是对本类类型对象的引用(一般常用const修饰)&#xff0c;在用已存在的类类型对象创建新对象时由编译器自动调用。 特点&#xff1a; 拷贝构造函数是构造函数的一个重载形…

K8s-网络原理-上篇

引言 本文是学习《深入剖析K8s》网络原理部分的学习笔记&#xff0c;相关图片和案例可以从https://github.com/WeiXiao-Hyy/k8s_example获取&#xff0c;欢迎Star&#xff01; 网络基础 IP组成 IP地址由两部分组成&#xff0c;即网络地址和主机地址。网络地址表示其属于互联…

GB28181视频汇聚EasyCVR平台接入海康Ehome设备,设备在线但是视频无法播放是什么原因?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

vivado 物理优化约束、交互式物理优化

物理优化约束 Vivado Design Suite在物理优化过程中尊重DONT_TOUCH特性。它不在具有这些属性的网络或小区上执行物理优化。要加快网络选择过程中&#xff0c;具有DONT_TOUCH属性的网络经过预过滤&#xff0c;不被考虑用于物理优化。此外&#xff0c;还遵守Pblock分配&#xff…

MySQL与金蝶云星空对接集成SELECT语句连通销售订单新增(销售订单集成测试)

MySQL与金蝶云星空对接集成SELECT语句连通销售订单新增(销售订单集成测试) ​​ ​​ 数据源系统:MySQL MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQLAB公司开发&#xff0c;属于Oracle旗下产品。MySQL是最流行的关系型数据库管理系统之一&#xff0c;在WEB应用方…

Nginx实现原理全解析:构建高效Web服务器的关键

1、Nginx是什么 Nginx&#xff08;engine X&#xff09;是一个开源的轻量级的HTTP服务器&#xff0c;能够提供高性能的HTTP和反向代理服务。与传统的Apache服务器相比&#xff0c;在性能上Nginx占用系统资源更小、支持高并发&#xff0c;访问效率更高&#xff1b;在功能上&…

Linux课程_____网络管理

一、查看接口信息 1. ifconfig 查看所有活动网络接口的信息 ifconfig -a 查看所有网络接口信息 ifconfig 直接加网络接口 查看指定网络接口信息 1.1查看指定接口IP [rootlocalhost ~]# ip addr show ens160 1.2设置网络接口的IP地址 # ifconfig eth0 192.168.152.133 …

(一)Linux+Windows下安装ffmpeg

一丶前言 FFmpeg是一个开源的音视频处理工具集&#xff0c;由多个命令行工具组成。它可以在跨平台的环境中处理、转换、编辑和流媒体处理音视频文件。 FFmpeg支持多种常见的音视频格式和编解码器&#xff0c;可以对音视频文件进行编码、解码、转码、剪辑、合并等操作。它具有广…

HQYJ 2024-3-14 作业

TCP通信三次握手和四次挥手&#xff1a; 并行和并发的区别&#xff1a;并发是单核处理器处理多个线程任务&#xff0c;并行是多核处理器同时处理多个线程任务。并发过程中会抢占CPU资源&#xff0c;轮流使用&#xff1b;并行过程不会抢占CPU资源。 阻塞IO和非阻塞IO&#xff…

HarmonyOS开发:超详细介绍如何开源静态共享包,实现远程依赖

前言 当我们开发了一个独立的功能&#xff0c;想让他人进行使用&#xff0c;一般的方式就是开源出去&#xff0c;有源码的方式&#xff0c;也有文件包的形式&#xff0c;当然了也有远程依赖的方式&#xff0c;比如在Android中&#xff0c;我们可以提供源码&#xff0c;也可以打…

开源离线语音识别输入工具CapsWriter v1.0——支持无限时长语音、音视频文件转录字幕。

分享一款开源离线语音识别输入工具&#xff0c;支持无限时长语音、音视频文件转录字幕。 软件简介&#xff1a; CapsWriter是一款免费开源且可完全离线识别的语音输入工具&#xff0c;无需担心因在线版本识别带来的各种隐私泄露问题。支持win7及以上的系统&#xff0c;已经更…

华为中心AP 配置入侵防御实验

配置入侵防御示例 组网图形 图1 入侵防御组网图 组网需求配置思路操作步骤中心AP的配置文件 组网需求 如图1所示&#xff0c;某企业部署了WLAN网络&#xff0c;内网用户可以访问Internet的Web服务器。现需要在中心AP上配置入侵防御功能&#xff0c;具体要求如下&#xff1a; 保…